likes
comments
collection
share

svg与图标icon

作者站长头像
站长
· 阅读数 60

可缩放矢量图形(Scalable Vector Graphics , SVG) ,作为一种矢量图 ,它最大的特性是可以随意放大而不失真

矢量图是根据几何特性来绘制图形,矢量可以是一个点或一条线,矢量图只能靠软件生成,文件占用内在空间较小,因为这种类型的图像文件包含独立的分离图像,可以自由无限制的重新组合。它的特点是放大后图像不会失真,和分辨率无关,适用于图形设计、文字设计和一些标志设计、版式设计等。

Inline Svg

Html 为我们提供了 Inline Svg 标签,我们可以很容易的将 svg 图片嵌入 html 页面中:

<!DOCTYPE html>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <svg viewBox="0 0 1024 1024" width="48" height="48">
        <path
            d="M923 283.6c-13.4-31.1-32.6-58.9-56.9-82.8-24.3-23.8-52.5-42.4-84-55.5-32.5-13.5-66.9-20.3-102.4-20.3-49.3 0-97.4 13.5-139.2 39-10 6.1-19.5 12.8-28.5 20.1-9-7.3-18.5-14-28.5-20.1-41.8-25.5-89.9-39-139.2-39-35.5 0-69.9 6.8-102.4 20.3-31.4 13-59.7 31.7-84 55.5-24.4 23.9-43.5 51.7-56.9 82.8-13.9 32.3-21 66.6-21 101.9 0 33.3 6.8 68 20.3 103.3 11.3 29.5 27.5 60.1 48.2 91 32.8 48.9 77.9 99.9 133.9 151.6 92.8 85.7 184.7 144.9 188.6 147.3l23.7 15.2c10.5 6.7 24 6.7 34.5 0l23.7-15.2c3.9-2.5 95.7-61.6 188.6-147.3 56-51.7 101.1-102.7 133.9-151.6 20.7-30.9 37-61.5 48.2-91 13.5-35.3 20.3-70 20.3-103.3 0.1-35.3-7-69.6-20.9-101.9z">
        </path>
    </svg>
</body>

</html>

svg与图标icon

注释:

  • viewBox 定义了 svg 的视图容器,后文详细讨论 -> viewBox .
  • svg 是由点和线构成的矢量图,path 属性定义了这些点和线的位置。

因为矢量图的特点,我们可以很容易的改变这些点和线的颜色,从而非常容易的修改 svg 图片的颜色:

 <svg viewBox="0 0 1024 1024" width="48" height="48">
        <path
            fill="red"
            d="...">
        </path>
    </svg>

svg与图标icon

进一步,我们可以将这个 svg 图标自定义为一个组件来使用,比如用 react :

// heart.tsx
import React from "react"

interface IProps {
    color: string
}

const HeartIcon : React.FC<IProps> = (props) => {
    const color = props.color
    return (
        <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4014" data-darkreader-inline-fill="" width="48" height="48"><path ... fill={color}></path></svg>
    )
}

export default HeartIcon

批量图标

阿里巴巴矢量图标库对项目量级的图标引入提供了两种解决方案,第一种是使用 @font-face ,它允许我们为项目引入自定义的字体文件。并且可以通过 font-family 使用自定义字体。

图标和字体本质上都可以用点和线来构成,因此各个 os 的字体系统本身就支持一些图标的表示,比如 windows就可以直接输入 emoji . 因此,只要引入图标生成的字体文件,我们就可以在项目中使用这些图标了。

@font-face {font-family: 'iconfont';
    src: url('iconfont.eot');
    src: url('iconfont.eot?#iefix') format('embedded-opentype'),
    url('iconfont.woff') format('woff'),
    url('iconfont.ttf') format('truetype'),
    url('iconfont.svg#iconfont') format('svg');
}

.iconfont{
    font-family:"iconfont" !important;
    font-size:16px;font-style:normal;
    -webkit-font-smoothing: antialiased;
    -webkit-text-stroke-width: 0.2px;
    -moz-osx-font-smoothing: grayscale;}
<i class="iconfont">&#x33;</i> 

而另一种引入批量图标的方式就是我们本文讨论的 svg 方式。它的核心是先通过 symbol 元素定义 svg 模板,然后在要使用图标的地方使用 use元素 copy 之前定义的模板,渲染 svg 图标。它看起来像这样:

<body>
    <svg style="border: 1px solid #000;">
        <symbol id="myDot" viewBox="0 0 1024 1024" width="48" height="48">
            <path ...>
            </path>
        </symbol>
    </svg>

    <svg>
        <use href="#myDot" fill="red"  />
    </svg>
</body>

svg与图标icon

我们接下来要解决几个疑问:

  • 我们的模板 symbol 虽然没有显示,但它的容器 svg 却默认占了一块空间,我们用 border 将其标注了出来。怎么怎么将其隐藏掉呢?
  • viewBox 属性是干什么用的呢?如果你通过搜索引擎去查找API文档,你会发现它的四个值分别是 【min-xmin-ywidth and height】 ,min-x,min-y 是什么? 我们不是已经定义了 symbol 元素的 width,height 属性了吗?为什么在 viewBox 中又要在定义一次?
  • 我们如何自定义 svg 的颜色和大小呢?
  • use 是在什么范围内寻找对应的模板标签 symbol 呢?

接下来我们一一讨论这些疑问。

额外的svg

这是个很容易解决的问题,通过绝对定位将其隐藏掉:

    <svg style="position: absolute;width: 0;height: 0;overflow: hidden;">
        <symbol id="myDot" viewBox="0 0 1024 1024" width="48" height="48">
	        <path ...>
            </path>
        </symbol>
    </svg>

viewBox

viewBox 的作用实际上可以分成:【截图】+【缩放】。

比如上面的 symbol 元素,它的容器大小是 width="48" height="48" . 那么 viewBox = "0 0 1024 1024" 的意思是:从坐标轴x = 0 , y = 0 出发,从 path 元素截取长1024px,宽1024px 的内容, 并将其按容器大小(长宽48px)等比缩放,放入容器中。

关于更详细的 viewBox 的解释可以阅读 svg中viewbox图解分析 这篇文章。

如何自定义 svg 的颜色和大小?

use 元素会 copy 一份 symbol 元素,并将其属性并入拷贝的 symbol 元素上。因此我们可以在 use 上定义 fill 属性来定义 svg 的填充颜色:

<use href="#myDot" fill="red"  />

它会转换为:

<symbol id="myDot" fill="red" viewBox="0 0 1024 1024" width="48" height="48">
</symbol>

symbol 内的 path 元素会继承 symbolfill 属性,因此 path 上的点和线也拥有了填充的颜色。

类似的,我们也可以在 use 上定义 width,height 属性,但要注意 svg 在这里是一个容器,我们通常要定义容器和里面图案两者的大小。比如 svg 的默认长是 300 px ,宽是 150 px,如果我们把图案设置的过大:

    <svg>
        <use href="#myDot" fill="red" width="512" height="512" />
    </svg>

svg与图标icon

因此我们还要给 svg 加上 class 样式:

    <svg style="width: 512px;height: 512px;">
        <use href="#myDot" fill="red" width="512" height="512" />
    </svg>

use 是在什么范围内寻找对应的模板标签 symbol 呢?

根据 MDN的介绍:

The use of <symbol> elements for graphics that are used multiple times in the same document adds structure and semantics.

简单来说,只要在同一文档下,<symbol> 元素可以被实例化多次来使用。这一点在我们使用 use 时也可以发现,它是通过 document.getElementById 的语法糖 # 来匹配对应的 symbol 的,因此当然它的查找范围是同一个 html 文件,同一个文档内。

阿里巴巴矢量图标库具体是如何使用 svg 引入批量图标的?

它会为图标库生成一个 JavaScript 脚本文件,简化后大概为:

!(function (l) {
  var e,
    d =
      '<svg><symbol id="iconadd1" viewBox="0 0 1024 1024"><path ... ><path /></symbol><symbol id="iconadd1" viewBox="0 0 1024 1024"><path ... ><path /></symbol><symbol id="iconadd1" viewBox="0 0 1024 1024"><path ... ><path /></symbol></svg>',
    t = (e = document.getElementsByTagName("script"))[
      e.length - 1
    ].getAttribute("data-injectcss");
  if (t && !l.__iconfont__svg__cssinject__) {
    l.__iconfont__svg__cssinject__ = !0;
    try {
      document.write(
        "<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>"
      );
    } catch (e) {
      console && console.log(e);
    }
  }
  (function () {
    var e, t, n, i, o, a;
    ((e = document.createElement("div")).innerHTML = d),
      (d = null),
      (t = e.getElementsByTagName("svg")[0]) &&
        (t.setAttribute("aria-hidden", "true"),
        (t.style.position = "absolute"),
        (t.style.width = 0),
        (t.style.height = 0),
        (t.style.overflow = "hidden"),
        (n = t),
        (i = document.body).firstChild
          ? ((o = n), (a = i.firstChild).parentNode.insertBefore(o, a))
          : i.appendChild(n));
  });
})(window);

注释:

  • d 表示定义 svgsymbol , 它被 innerHTML 写入一个创建的 div
  • 从这个 div 中取出刚刚定义的 svg 元素,并设置绝对定位,隐藏模板 svg .
  • 将这个 div 插入 document.body

因此我们在 SPA 中引入这个 js 文件,因为整个 SPA 项目只有一个 document,因此我们能够在整个项目中通过 use 引入这些图标。


参考:

转载自:https://juejin.cn/post/7247045913980764215
评论
请登录