应该了解的 Web 图标解决方案

A picture is worth a thousand words, 一图胜千言。 没错,从 Web 诞生的那天开始,图标就成为视觉层面不可或缺的一个元素,在一个 Web 页面中,一个图标不仅仅能从视觉上带来优雅感,更重要的是,它对此处的功能起到了点睛之笔的作用,它会使得用户更容易理解你的产品。那么,在我们当下的 Web 前端开发中,最常见的图标解决方案有哪些呢?大概是三种,图片、IconFont 和 Svg。图片就不说了,就是整一坨小的 png 图片作为图标,最终把他们合在一个图片里,此种技术还有一个好听的名字 CSS Sprites,国人称为 雪碧图,此种方案还是 Web 前端性能优化军规之一,降低 http 请求数来达到提速的目的。

图片咱们今天不说了,没啥意思。咱们今天聊聊 IconFont 和 inline SVG,然后把这两个方案的优劣进行一个对比,然后再介绍介绍常见的 IconFont 库及 inline SVG 的库,最后再展示一个小 Demo 给大家看一看具体在页面上 IconFont 和 Svg 有什么不同。

IconFont 介绍

IconFont 使用的技术是 CSS 自定义字体,用户可以把图标集合打包成字体文件 ( 如何打包,可使用 iconfont.cn ),然后通过 @font-face 来自定义一个字体,最后通过设置 font-family 以及通过使用图标字体的 unicode编码 来使用图标。

在 CSS 里声明字体,编写 unicode 编码对应的图标:

@font-face {
  font-family: 'FontAwesome';
  src: url('../fonts/fontawesome-webfont.eot?v=4.6.3');
  src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'),         
        url('../fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'), 
        url('../fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'), 
        url('../fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'), 
        url('../fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg');
  font-weight: normal;
  font-style: normal;
}

.fa {
 font-family: 'FontAwesome';
 display: inline-block;
}

.fa-icon:after {
  content: '\f00c'
}

在 HTML 里这么写就可以了:

<i class="fa-icon"></i>

IconFont 有大量的开源解决方案,而且有很多现成的图标,比较具有代表性的如下:

  • FontAwesome,具备完善大量的图标库,对于定制化程度不高的项目,可以直接拿过来用
  • Iconfont.cn,阿里的解决方案,不但有现成的图标供你选择,还可以上传自己的图标来制作 IconFont

IconFont 的最大的好处就是浏览器兼容性好(IE6+),可以通过 CSS 来控制图标大小、颜色。

inline SVG 介绍

使用 IconFont 是把已有的矢量文件(通常是很多 .svg 文件)打包成字体文件,而 inline SVG 则是把 .svg 文件合并成一个大的 .svg 文件,然后在 HTML 中引用这个文件即可,具体步骤参考下面。

合并 svg

在这里搞了三个 svg 文件,准备把他们合并在一起:

SVG Symbol

我这里使用的是 svg-symbol 方案来合并 svg。

还有一个合并方法是 SVG defs,这个比 SVG Symbol 要鸡肋很多,在此就不介绍了。

通过使用 gulp-svg-symbols 来把 svg 文件合并:

var gulp       = require('gulp');
var svgSymbols = require('gulp-svg-symbols');

gulp.task('sprites', function () {
  return gulp.src('assets/svg/*.svg')
    .pipe(svgSymbols())
    .pipe(gulp.dest('assets'));
});

最终得到的 svg 文件:

<svg xmlns="http://www.w3.org/2000/svg" style="width:0; height:0; visibility:hidden;">
    <symbol id="circle" viewBox="0 0 200 200">
      <g class="transform-group">
        <g transform="scale(0.1953125, 0.1953125)">
          <path d="..." fill="#272636"/>
        </g>
      </g>
    </symbol>
    <symbol id="password" viewBox="0 0 200 200">
      <g class="transform-group">
        <g transform="scale(0.1953125, 0.1953125)">
          <path d="..." fill="#272636"/>
        </g>
      </g>
    </symbol>
    <symbol id="profile" viewBox="0 0 200 200">
      <g class="transform-group">
        <g transform="scale(0.1953125, 0.1953125)">
          <path d="..." fill="#272636"/>
        </g>
      </g>
    </symbol>
</svg>

使用方法

在 HTML 文件中声明 svg,然后通过 <svg><use xlink:href="#id" /></svg> 来使用:

<svg xmlns="http://www.w3.org/2000/svg" style="width:0; height:0; visibility:hidden;">
    <symbol id="circle" viewBox="0 0 200 200">
      <g class="transform-group">
        <g transform="scale(0.1953125, 0.1953125)">
          <path d="..." fill="#272636"/>
        </g>
      </g>
    </symbol>
    <symbol id="password" viewBox="0 0 200 200">
      <g class="transform-group">
        <g transform="scale(0.1953125, 0.1953125)">
          <path d="..." fill="#272636"/>
        </g>
      </g>
    </symbol>
    <symbol id="profile" viewBox="0 0 200 200">
      <g class="transform-group">
        <g transform="scale(0.1953125, 0.1953125)">
          <path d="..." fill="#272636"/>
        </g>
      </g>
    </symbol>
</svg>

<svg class="icon"><use xlink:href="#profile" /></svg>
<svg class="icon"><use xlink:href="#password" /></svg>
<svg class="icon"><use xlink:href="#circle" /></svg>

你也可以通过 <svg><use xlink:href="http://cdn.com/assets/symbols.svg#id" /></svg> 来直接使用存储在 CDN 上的 svg 文件,如果感觉每个都要写 CDN 的地址太麻烦,则可以封装 JS 工具,统一维护,统一管理。

inline SVG 目前没有什么特别推荐的开源解决方案,一般情况下,图标都是自己的,自己通过工具打包就已经很方便了,而且很难通过纯 CSS 或 JS 来解决,因为它跟 HTML 的关联性太大了,即使是这样,还是推荐一个库给大家了解了解:

SVGInjector

IconFont 与 inline SVG 方案对比

浏览器兼容性

IconFont inline SVG
IE6+ IE9+ , Android 3.0+ 移动端支持很好,现在可以使用

尺寸、颜色是否容易控制

IconFont inline SVG
浏览器会认为它是一个字体
因此只能使用 color 和 font-size 控制,而且尺寸特别不精细
支持多色、局部颜色控制、控制尺寸使用 width 和 height

访问的稳定性

IconFont inline SVG
Font 在 CDN 上会有跨域问题;而且字体下载不下来是很常见的事;
还有一些已知的Chrome的Bug
貌似代理性质的浏览器,像 UC ,就不支持自定义 Font;
一些浏览器拦截插件会拦截自定义字体......
Svg很正常

语义化

IconFont inline SVG
根本不语义化,你要写多余没有意义的标签,对 SEO 很不利 Svg 是图形,人家就是图形,而且 SVG Symbol 支持 title 和 description 属性,非常友好

用起来是否顺滑

IconFont inline SVG
自己生成 svg 然后使用工具打包成多个字体文件,然后用 unicode 对应使用 SVG Symbol 使用打包工具生成 SVG 集合,直接通过 ID 使用

IconFont 与 SVG 的 Demo

总结

如果,你的产品需要支持 IE8 及以下,还是推荐使用 IconFont ,因为使用 SVG Symbol 的话,你需要考虑在低端浏览器下的兼容性,常见的做法是,生成一些 png 的图片做 fallback,然后在低端浏览器下显示,把 svg 隐藏.....

如果,你只需要考虑 IE9+ 和 Android 3.0 + ,毫无疑问,inline SVG 是唯一选择!