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方案並行的狀況,所以在實際的開發過程當中應具體問題具體分析。


參考連接:

相關文章
相關標籤/搜索