Web Icon 实现方案总结

原文地址: Web Icon 实现方案总结。若有描述不妥之处,欢迎指正。

Icon,在界面设计中,具备指代意义的图形符号。在前端开发中,图标每每由 UI 设计给出,而后经前端开发人员在 html 中使用。Icon 的设计和使用在近几年的发展中,也经历了由当初的 img 方案 到现现在的 svg 方案。下文将从 Icon 的发展历程以及到现现在的 svg sprite 技术给出具体的介绍。css

Icon 发展历程

初期 img 的方案

小时候,咱们都是这样在前端应用一个 icon 的:html

  1. 网上找到相关的图片资源或者由视觉同窗给出
  2. 下载到本地 assets/img 文件夹, 重命名为 index.png
  3. 页面上使用 <img src="assets/img/index.png" />,或者直接引用一个远程的图片地址
  4. 使用 css 控制 img 标签的样式

还不曾长大,有人就说了,孩子,你这样不行啊。页面中要是有不少 Icon 的话岂不请求中都是图片请求。听大人一说,当初的本身吓得抓紧百度了一下。有一篇文章《Best Practices for Speeding Up Your Web Site》,也就是如今你们熟知的雅虎前端性能优化准则。第一条:前端

Minimize HTTP Requests (减小/最小化 http 请求数)

显然由于Icon的展现,就致使了页面中那么多的 http 请求,有点不合情理,大人说的很对啊。vue

因而乎,又打开了百度,怎么优雅的使用的icon呢? css sprite ,这是个什么鬼?android

CSS Sprites are a means of combining multiple images into a single image file for use on a website, to help with performance.
( CSS Sprites 是为优化性能而将多个图片合并到一个图片在网站中使用的方式。)

有了 css sprite, 前端在使用时,只请求一次图片,经过 css 的 background-imgbackground-position 属性控制显示 icon。很显然,减小了 http 的请求次数,终于又能够开开心心的玩耍了。webpack

然而还没开心多久,蛋疼的事情来了。项目中其余功能须要添加额外的 icon,UI 只能是从新再搞图,若是不会影响以前 icon的位置还好,如果调整了以前icon 的位置,之前写的css又得无奈的改动。git

若是每次这样的修改都让 UI 给调整,估计 UI 早早的就疯了。还好社区内很快出现了相应的工具来自动化的完成这些工做。Sprity 一个根据相应配置自动合成 sprite 图的工具,在当时也提供了基于Gulp/Grant 的插件。很显然这种技术在目前已经不怎么实用了,Sprity github 上的项目最近一次提交也已是2年前的事情了。《CSS Sprites: What They Are, Why They’re Cool, and How To Use Them》 这篇文章中给出了在当时如何使用工具自动生成 sprite 图片的方法。github

在当时为了优化性能,还有一种技术 Data URIs,它能够将图片编码后内联于样式表中,避免了额外的 http 请求,同时还能避免配置 background-position。为优化性能引入的这种技术,可能还存在必定的性能问题,具体能够查看这篇文章给出的论述: 《Data URIs》web

Icon Font

Web Font 的发展得益于 CSS3 的@font-face属性。vue-cli

容许网页开发者为其网页指定在线字体。 经过这种做者自备字体的方式,@font-face 能够消除对用户电脑字体的依赖。

Icon Font 的思想来自于 Web Font,使用字体的方式设计 Icon。其中阿里巴巴开源的图标库 IconFont 应用普遍。下面以 Iconfont 图标库为例,介绍其使用方法。

登陆 IconFont 后能够搜索本身想要的图标并添加至购物车,购物车中的图标能够添加至项目。Iconfont 提供了以项目进行管理图标的功能。项目中的 web 端图标使用方式有三种:Unicode, Font Class, Symbol。

Unicode引用

unicode 是字体在网页端最原始的应用方式,特色是:

  • 兼容性最好,支持 ie6+,及全部现代浏览器。
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • 可是由于是字体,因此不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。

使用步骤:

第一步:拷贝项目下面生成的 font-face

@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 的样式

.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>

font-class引用

font-class 是 unicode 使用方式的一种变种,主要是解决 unicode 书写不直观,语意不明确的问题。

与 unicode 使用方式相比,具备以下特色:

  • 兼容性良好,支持 ie8+,及全部现代浏览器。
  • 相比于 unicode 语意明确,书写更直观。能够很容易分辨这个 icon 是什么。
  • 由于使用 class 来定义图标,因此当要替换图标时,只须要修改 class 里面的 unicode 引用。
  • 不过由于本质上仍是使用的字体,因此多色图标仍是不支持的。

使用步骤以下:

第一步:拷贝项目下面生成的 fontclass 代码:

//at.alicdn.com/t/font_8d5l8fzk5b87iudi.css

第二步:挑选相应图标并获取类名,应用于页面

<i class="iconfont icon-xxx"></i>

symbol 引用

这是一种全新的使用方式,应该说这才是将来的主流,也是平台目前推荐的用法。 这种用法实际上是作了一个svg的集合,与上面两种相比具备以下特色:

  • 支持多色图标了,再也不受单色限制。
  • 经过一些技巧,支持像字体那样,经过 font-size,color 来调整样式。
  • 兼容性较差,支持 ie9+,及现代浏览器。
  • 浏览器渲染 svg 的性能通常,还不如 png。

使用步骤以下:

第一步:拷贝项目下面生成的 symbol 代码:

//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js

第二步:加入通用 css 代码(引入一次就行):

<style type="text/css">
    .icon {
       width: 1em; height: 1em;
       vertical-align: -0.15em;
       fill: currentColor;
       overflow: hidden;
    }
</style>

第三步:挑选相应图标并获取类名,应用于页面:

<svg class="icon" aria-hidden="true">
    <use xlink:href="#icon-xxx"></use>
</svg>

Svg Icon

由于 IconFont 在显示图标方面的缺陷,开发者开始使用 SVG 做为其替代方案展现 Icon。

其中 Inline SVG vs Icon Fonts 一文中给出了详细的 Inline Svg 与 Icon Fonts之间的区别。

Iconfont主要的缺陷:

  1. 浏览器将其视为文字进行抗锯齿优化,不一样系统下对文字的渲染显示效果可能不一样
  2. Icon 做为字体进行显示时,其显示的大小、位置均可能会受到 font-size,line-height, word-spacing 等css属性影响,其容器的css样式也会可能影响到该字体icon的位置等。
  3. Iconfont 仅仅支持单色,且高分辨率下的显示效果不佳。

下面列举了项目中使用 SVG 的几种方式,各有优缺点:

Img/object 标签

早期使用 svg 的一种方式。缺点在于每一个图标都须要保存成一个 svg 文件,使用时单独请求。项目中图标过多的化会带来过多的 http 请求。

Inline svg

顾名思义,将 svg 直接写进 html,这种方法简单暴力,能够减小 http 的请求。

优势: 能够直接使用 class 进行 svg 的样式定制,可控性强

缺点: 复用性差,效率低

Data URIs

css 中直接使用 base64 编码后的 svg

.icon{ 
  background: url(data:text/svg+xml;base64,<base64 encoded data>)
}

优势: 不须要额外引用 SVG 文件

缺点:可控性差,没法使用 css 进行样式定制,可能会存在潜在的效率问题

svg sprite

初期最基础的 svg sprite 技术相似于 css sprite,经过
background-position 等属性控制其显示的位置。相似于css sprite, 社区中也出现了对应的工具和在线网站提供生成 svg sprite 的方法。

优势: 减小 http 请求,能够 fallback 到 css sprite

缺点: 可控性差,没法方便的经过css进行控制样式

基于svg symbols 的svg sprite

svg symbols 是定义 svg 引用的一种方式,基于该方式下的 svg sprite 是在传统 svg sprite 上的改进,改进了在 sprite 中获取单一 Icon 的调用方式,以前是根据位置,基于 svg symbol 下的调用是根据引用。

<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">

    <symbol id="circle-cross" viewBox="0 0 32 32">
      <title>circle-cross icon</title>
      <path d="M16 1.333q2.99 0 5.703 1.161t4.677 3.125 3.125 4.677 1.161 5.703-1.161 5.703-3.125 4.677-4.677 3.125-5.703 1.161-5.703-1.161-4.677-3.125-3.125-4.677-1.161-5.703 1.161-5.703 3.125-4.677 4.677-3.125 5.703-1.161zm0 2.667q-2.438 0-4.661.953t-3.828 2.557-2.557 3.828-.953 4.661.953 4.661 2.557 3.828 3.828 2.557 4.661.953 4.661-.953 3.828-2.557 2.557-3.828.953-4.661-.953-4.661-2.557-3.828-3.828-2.557-4.661-.953zm3.771 6.885q.552 0 .948.391t.396.943-.396.948l-2.833 2.833 2.833 2.823q.396.396.396.938 0 .552-.396.943t-.948.391-.938-.385l-2.833-2.823-2.823 2.823q-.385.385-.948.385-.552 0-.943-.385t-.391-.938q0-.563.385-.948l2.833-2.823-2.833-2.833q-.385-.385-.385-.938t.391-.948.943-.396.948.396l2.823 2.833 2.833-2.833q.396-.396.938-.396z"/>
    </symbol>

    <symbol id="circle-check" viewBox="0 0 32 32">
      <title>circle-check icon</title>
      <path d="M16 1.333q2.99 0 5.703 1.161t4.677 3.125 3.125 4.677 1.161 5.703-1.161 5.703-3.125 4.677-4.677 3.125-5.703 1.161-5.703-1.161-4.677-3.125-3.125-4.677-1.161-5.703 1.161-5.703 3.125-4.677 4.677-3.125 5.703-1.161zm0 2.667q-2.438 0-4.661.953t-3.828 2.557-2.557 3.828-.953 4.661.953 4.661 2.557 3.828 3.828 2.557 4.661.953 4.661-.953 3.828-2.557 2.557-3.828.953-4.661-.953-4.661-2.557-3.828-3.828-2.557-4.661-.953zm4.49 7.99q.552 0 .943.391t.391.943-.396.948l-5.656 5.656q-.385.385-.938.385-.563 0-.948-.385l-2.833-2.823q-.385-.385-.385-.948 0-.552.391-.943t.943-.391.948.396l1.885 1.885 4.708-4.719q.396-.396.948-.396z"/>
    </symbol>

    <!-- .... -->
</svg>

每一个Symbol设置一个id做为其引用的名字。

使用方法:

第一步: 将上述 svg 做为 body 的第一个子元素插入。

第二步: 在须要引用 icon 的地方经过 use xlink:href 的方式使用 svg

<svg class="icon">
  <use xlink:href="#circle-cross"></use>
</svg>

上述基于 Symbol 制做 svg-sprite 的方式,使用起来方便,经过使用 id 引用对应的svg,避免了使用background-position进行 svg 的引用。即便后期须要从新合并新的 svg-sprite,只须要合并先后对应svg的symbol id 不发生变化,合并先后业务中已经使用的 svg 就不用作任何变化。

基于 svg symbol 的 svg sprite Icon 展现方案如何集成到 webpack 工做流中

vue-svg-icon 这个 demo 中详细给出了基于 webpack 的 vue 单页面项目中如何继承 svg-icon 方案的步骤。

第一步:制做svg-sprite:

webpack 中添加 svg-sprite-loader, 并添加以下配置:

{
  test: /\.svg$/,
  include: [resolve('src/components/svg-icon/icons')],
  use: [
    {
      loader: 'svg-sprite-loader',
      options: {
        symbolId: 'icon-[name]'
      }
    }
  ]
},

svg-sprite-loader 将咱们引用的指定文件夹下的 svg 制做成 svg sprite 并插入 html 的 body 中。

须要注意,此处咱们应指定文件夹存放咱们项目中全部 svg icon,vue-cli 中还提供了 url-loader 处理 svg,所以咱们应添加以下配置,避免 icon 下的 svg 文件被 url-loader 处理。

{
    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    loader: 'url-loader',
    exclude: [resolve('src/components/svg-icon/icons')], // 默认不处理该文件夹的命中的文件
    options: {
        limit: 10000,
        name: utils.assetsPath('img/[name].[hash:7].[ext]')
    }
},

经过 exclude 的配置能够避免指定icon文件夹下的svg文件被url-loader处理。

第二步: 封装使用时的 svg component

上面给出了在生成 svg sprite 后,经过 use 使用 svg 的方法。为方便项目中引用,封装 SvgIcon.vue 组件。

<!-- svg-icon 组件,业务组件中直接使用该组件展现 icon -->
<template>
  <svg :class="svgClass" aria-hidden="true">
    <use :xlink:href="iconName"></use>
  </svg>
</template>

<script>

// 引入全部的svg的文件
const requireAll = requireContext => requireContext.keys().map(requireContext);
const req = require.context('./icons', false, /\.svg$/);
requireAll(req);

export default {
  name: 'svg-icon',
  props: {
    iconClass: {
      type: String,
      required: true,
    },
    className: {
      type: String,
    },
  },
  computed: {
    iconName() {
      return `#icon-${this.iconClass}`;
    },
    svgClass() {
      if (this.className) {
        return `svg-icon ${this.className}`;
      }
      return 'svg-icon';
    },
  },
};
</script>

<style scoped>
.svg-icon {
  width: 40px;
  height: 40px;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style>

使用svg sprite 可能会遇到的问题

svg文件精简的问题

为何要进行 svg 文件精简?

UI同窗经过工具导出的SVG文件一般包含大量冗余且无用的信息,如编辑器元数据,注释,隐藏元素,默认值以及其余能够删除且不影响svg正常渲染的内容。

相似iconmoon.ioIconfont 都提供了在线精简svg的功能。

在多人协做以及须要频繁改动svg文件的中大型项目中,显然这种依赖手动流程去精简svg的方法已经没法知足快速开发的须要。所以咱们须要在咱们的工做流中集成相似的精简svg的工具。

svgo(svg optimizer) 是一个基于Nodejs的svg文件优化工具,其经过一系列的配置项能够实现定制化的精简svg的需求。

svgo-loader 基于webpack以及svgo的用于优化svg的loader。

使用方式:

webpack.base.conf.js

// 引入svgo的配置文件
const svgoConfig = require('../config/svgo-config.json');

...

{
    test: /\.svg$/,
    include: [resolve('src/components/svg-icon/icons')],
    use: [
      {
        loader: 'svg-sprite-loader',
        options: {
          symbolId: 'icon-[name]'
        }
      },
      {
        loader: 'svgo-loader',
        options: svgoConfig,
      }
    ]
},

svgo-config.json(定义了精简svg的规则)

{
  "plugins": [
    { "cleanupAttrs": true },
    { "cleanupEnableBackground": true },
    { "cleanupIDs": true },
    { "cleanupListOfValues": true },
    { "cleanupNumericValues": true },
    { "collapseGroups": true },
    { "convertColors": true },
    { "convertPathData": true },
    { "convertShapeToPath": true },
    { "convertStyleToAttrs": true },
    { "convertTransform": true },
    { "mergePaths": true },
    { "removeComments": true },
    { "removeDesc": true },
    { "removeDimensions": true },
    { "removeDoctype": true },
    { "removeEditorsNSData": true },
    { "removeEmptyAttrs": true },
    { "removeEmptyContainers": true },
    { "removeEmptyText": true },
    { "removeHiddenElems": true },
    { "removeMetadata": true },
    { "removeNonInheritableGroupAttrs": true },
    { "removeRasterImages": true },
    { "removeTitle": true },
    { "removeUnknownsAndDefaults": true },
    { "removeUselessDefs": true },
    { "removeUnusedNS": true },
    { "removeUselessStrokeAndFill": true },
    {
      "removeAttrs": { "attrs": "fill"} //移除fill属性
    },
    { "removeXMLProcInst": true },
    { "removeStyleElement": true },
    { "removeUnknownsAndDefaults": true},
    { "sortAttrs": true }
  ]
}

状态相关的svg-icon以及多色问题的思考

在 web 开发的过程当中,咱们常常会遇到一些状态相关的 icon。好比,当用户 click 或者 hover 时,咱们须要对 icon 的颜色进行相应的变化。采用 Iconfont 方案时,由于其字体的本质,咱们能够直接对字体的颜色使用 css 进行控制。

svg-icon 方案的使用过程当中 svg 的颜色是一种填充色机制。经过fill属性将具体的路径进行颜色填充。如相关路径未指定fill属性,则其继承父元素的color属性进行填充。

所以 svg-icon 方案下状态颜色的变化分为如下两种状况:

  1. 多色 svg-icon 的状态变化非单一 path 的颜色变化,状态变化时,直接替换不一样状态下的 icon
  2. 多色 svg-icon 的状态变化仅仅涉及单一 path 下的颜色变化,此时能够经过该 path 的 fill 属性留空,经过css修改其父元素的 color 属性,达到修改 icon 颜色的目的。

总结

基于 svg-sprite 的svg-icon 方案在 14年的时候就已经出现,鉴于当时浏览器兼容性等缘由,并无获得大规模采用。现在随着技术的更新,兼容性显然已经不在是svg-icon应用的阻碍。移动端android 3.x, IE 9+ 均可以进行采用 svg-icon 的 icon 方案。经过调研,发现目前已经应用了 svg-icon 技术方案的有:

  • Github 全站采用了svg做为其icon展现方案, inline svg 的使用方式
  • iconfont 主站采用了inine svg 的方式,展现其全部的icon
  • 京东 部分icon (话费、机票等)采用了基于svg symbol的svg sprite 方案,展现icon
  • 腾讯视频 部分icon(暂停、播放)采用了基于svg symbol的svg sprite 方案

任何技术方案的讨论都脱离不了其应用场景。在实际的开发中,由于各类缘由,可能会存在多种icon方案并行的状况,所以在实际的开发过程当中应具体问题具体分析。


参考连接:

相关文章
相关标签/搜索