使用 SVG 代替 icon font

2022.01.13

在看 antd 的 icon 的时候,发现它在 3.9 版本之后就用 SVG 代替了 icon font 的实现了,为什么呢?除了两者兼容性差不多,icon font 还有那么一些缺点,主要还是因为它是字体导致的,使用字体,就意味着,

  1. 如果字体被 block 了,那么你的 icon 可能就会显示不正常了。
  2. 还有一些人会使用一些浏览器插件,强制修改字体,就会导致你的字体显示不正常了。
  3. 因为是字体,所以会有锯齿(aliasing),而 SVG 是纯矢量图,所以不会出现这种情况。

其他的一些使用上的区别,

  1. 定位。你需要使用 line-height,vertical-align 等的属性来调整位置。
  2. 大小。icon font 的话需要通过 font-size,而 SVG 的话,你可以通过修改 width,height 属性。

下面我们来具体通过例子来讲下两种方案。

Icon font

我们先来看下使用 icon font 的场景吧,写了个 demo,可以在这里看下,

https://carywill.github.io/Frontend/icon-font/index.html
https://carywill.github.io/Frontend/icon-font/index.html

来看下具体是怎么实现的,首先这个 安卓 的图标,它是通过给 <i> 标签添加一个 class,在 CSS 里给添加的 class 加一个 ::before 的伪类,在伪类里面,将 content 属性设置为在字体里面 安卓图标 对应的 unicode code point 的值就可以渲染出安卓图标了。

// css
.fa-android:before {
  content: "\f17b";
}

// html
<head>
  <link href="./all.css" rel="stylesheet" />
</head>

<body>
  <span>icons</span>
  <i class="fab fa-android"></i>
</body>

这里简单聊下字体和 unicode 的关系,可以在 unicode 官网 的 Interpreting Characters and Rendering Glyphs 章节看到如下一段话,

The character identified by a Unicode code point is an abstract entity, such as "LATIN CHARACTER CAPITAL A" or "BENGALI DIGIT 5." The mark made on screen or paper—called a glyph—is a visual representation of the character.

意思就是,unicode 规定了每一个 code point 所对应的符号是什么意思,而字体决定了 unicode 的每一个 code point 对应的符号是怎么渲染的。

拿大写的字母 A 来说,它在 unicode 里的 code point 是 0041,表示的是 LATIN CHARACTER CAPITAL A,可在 这里 查看。

而各种字体对这个 code point 0041 制作的 glyph 是不一样的,比如下图,我测试下下面 2 种字体里面的字母 A,

对了,像 icon font 这种通过字体自定义 icon 的,一般是使用 unicode 里面的 Private Use Area(PUA)区域,这一块区域没有规定 code point 的语义,所以你自定义 icon 可以利用这一块的 code point,关于 PUA 的知识可以看下这里


所以这里,我们使用了 unicode 的 \f17b 这个 code point,所以肯定字体里面这个 code point 对应的符号(glyph)肯定就是安卓图标了,我们可以在 CSS 文件里看到这个 <i> 标签的字体是 fa-brands-400,我们可以将该字体文件 fa-brands-400.woff 拖到这个网站 上去,你可以看到这个字体里面的每一个 glyphs 对应的 unicode code point,就比如我们的安卓图标,

SVG

Scalable Vector Graphics (SVG) are an XML-based markup language for describing two-dimensional based vector graphics.

常用的有 <path> 标签,比如 antd 3.9 之后的推特图标,

  <svg viewBox="64 64 896 896" focusable="false" class="" data-icon="twitter"
    width="1em" height="1em" fill="currentColor" aria-hidden="true">
    <path
      d="M928 254.3c-30.6 13.2-63.9 22.7-98.2 26.4a170.1 170.1 0 0 0 75-94 336.64 336.64 0 0 1-108.2 41.2A170.1 170.1 0 0 0 672 174c-94.5 0-170.5 76.6-170.5 170.6 0 13.2 1.6 26.4 4.2 39.1-141.5-7.4-267.7-75-351.6-178.5a169.32 169.32 0 0 0-23.2 86.1c0 59.2 30.1 111.4 76 142.1a172 172 0 0 1-77.1-21.7v2.1c0 82.9 58.6 151.6 136.7 167.4a180.6 180.6 0 0 1-44.9 5.8c-11.1 0-21.6-1.1-32.2-2.6C211 652 273.9 701.1 348.8 702.7c-58.6 45.9-132 72.9-211.7 72.9-14.3 0-27.5-.5-41.2-2.1C171.5 822 261.2 850 357.8 850 671.4 850 843 590.2 843 364.7c0-7.4 0-14.8-.5-22.2 33.2-24.3 62.3-54.4 85.5-88.2z">
    </path>
  </svg>

具体想要了解 SVG 的使用的话可以看下 这里

SVG 还可以实现一些 icon font 做不到的事情,比如描边。

结论:在两者中做选择的话,选 SVG,yyds。

参考

  1. <symbol>
  2. Use React SVG components for icons instead of an icon font
  3. feat(Icon) SVG Icon & Custom Icon
  4. Inline SVG vs Icon Fonts [CAGEMATCH]
  5. Seriously, Don’t Use Icon Fonts
  6. font awesome
  7. font-smooth - MDN
  8. Private-Use Characters, Noncharacters & Sentinels FAQ
  9. SVG: Scalable Vector Graphics Getting Started with SVG
  10. What is code point?- MDN