SVG 圖標制作指南

ercjd 8年前發布 | 35K 次閱讀 SVG 前端技術

譯注:譯文已獲得作者授權,轉載請附上原文鏈接,本文中 SVG icon 皆可用瀏覽器開發工具查看源碼和實現。

現在有很多種方法在網頁中使用 SVG 圖標,我并沒有把它們全部嘗試一遍,我將要介紹的方法是我們 Kaliop 的前端團隊所使用的,目前能夠很好的滿足我們的開發需求,比如:

  • 基于大型 CMS 系統的內容管理網站(非全棧 JS 的 Web App);
  • 圖標通常簡單且單色(可能根據網站內容和交互來使用不同的顏色),也有可能是單個圖標有兩種不同顏色;
  • 支持 IE9+。

本文內容將按照以下展開:

譯注: 在 Webpack 中使用 SVG 圖標 是譯者擴展的。

第一步:準備圖標

當你從設計師那里或者繪圖工具(如 Illustrator、 Adobe Assets、 Sketch、 Inkscape 等)中拿到 SVG 圖標時,你可能會直接放到網頁中,但是,如果能把圖標(用你常用的處理工具)稍微處理下,可以避免不少頭疼的問題。

圖標在Illustrator (左) 和 Sketch (右)的畫板上顯示效果

新建一個文件或畫板

在常用的繪圖工具中新建一個文件或者畫板,將圖標復制粘貼到中間,最好確保圖標是純凈的,沒有隱藏圖層。

正方形更好

圖標不需要非得是正方形的,除非圖標太寬或者太高,否則還是建議做成正方形的圖標,更好處理。當你有像素級的需求時,比如想要在低分辨率屏幕上獲得更好的顯示效果,就需要確定圖標尺寸。比如圖標需要適應 15x15 px 的網格,而且用的時候也多是這個尺寸時,就應該去創建 15x15 px 的畫板。不確定的時候,一般建議選擇 20x20 的尺寸。

毛邊問題

在邊緣區域留一點點空白,特別是對圓形圖標。瀏覽器渲染 SVG 時會做抗鋸齒處理,但是,有時抗鋸齒產生的額外像素點會跑到 viewBox 的外面,從而導致圖標的邊緣看上去被切掉了一部分,看起來有點方。

圖標邊緣未做留白處理,所以可能邊緣渲染出方形的邊,當瀏覽器對 SVG 的渲染不給力時,效果更糟糕。

因此,每次處理 16px 或 20px 的圖標時,要記得在每個邊緣留 0.5px 或 1px 的空白,還要記得導出整個畫板,而不是選中位于中間的路徑,否則邊緣的留白是不會導出。

導出 SVG

  • 在 Illustrator 中,選擇 ‘Save As’ 并選擇格式為 ‘SVG’(也許選擇 ‘Export as…’ 會更好)。
  • 在 Sketch 中,先選中畫板,點擊右下角 ‘Make Exportable’,并選擇格式為 ‘SVG’。
  • 在 Inkscape 中,選擇 ‘Save As’ 并選擇格式為 ‘Optimized SVG’。

關于 SVG 的知識點

你可能學習過關于 SVG 的基礎知識,并且能讀懂 SVG 的結構。至少你知道:

  • SVG 元素:  <svg> , <symbol>  , <g> ,  <path>
  • SVG 屬性:  d ,  fill ,  stroke ,  stroke-width

注意:從繪圖工具中導出的 SVG 經常帶著一些不必要的內容和標簽等(其中 d 下面包含了清晰的路徑數據),可以使用工具比如 SVGOMG ,然后比較一下處理前后哪些東西是移除或簡化過的。

移除顏色數據

對于單色圖標,確保:

  1. 在源文件中, path 的顏色都是黑色 (#000000) 。
  2. 在導出的 SVG 文件中,沒有  fill 屬性。

如果我們在 SVG 文件中設置了 fill 屬性,就不能通過 CSS 來改變顏色了,所以最好把顏色相關數據都刪掉,至少對單色的圖標這樣。

Illustrator 導出的SVG 中 path 都是黑色( #000000 )且不帶  fill 屬性,但 Sketch 導出的文件會帶,得自己手動刪掉像 fill="#000000" 這種屬性。

第二步:制作 SVG sprite

這一部分會包含不少代碼,但內容其實并不復雜。我們將創建包含多個 <symbol> 元素的 SVG 文件,每個 <symbol> 都有 id 和 viewBox 屬性,且包含圖標的 <path> 元素(或者其他元素如 <circle> 、 <rect> 等)。

我將這個 SVG 文件稱為 SVG sprite(參考 sprites in computer games 和 CSS),也可以被稱為 sprite sheet 或者 symbol store。

下面的 SVG sprite 中僅包含一個圖標:

<svg xmlns="http://www.w3.org/2000/svg">
  <symbol id="cross" viewBox="0 0 20 20">
    <path d="M17.1 5.2l-2.6-2.6-4.6 4.7-4.7-4.7-2.5 2.6 4.7 4.7-4.7 4.7 2.5 2.5 4.7-4.7 4.6 4.7 2.6-2.5-4.7-4.7"/>
  </symbol>
</svg>

往 SVG sprite 中添加圖標

下面是 Illustrator 導出的 SVG 圖標的代碼:

<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
  viewBox="0 0 15 15" style="enable-background:new 0 0 15 15;" xml:space="preserve">
  <path id="ARROW" d="M7.5,0.5c3.9,0,7,3.1,7,7c0,3.9-3.1,7-7,7c-3.9,0-7-3.1-7-7l0,0C0.5,3.6,3.6,0.5,7.5,0.5 C7.5,0.5,7.5,0.5,7.5,0.5L7.5,0.5L7.5,0.5z M6.1,4.7v5.6l4.2-2.8L6.1,4.7z"/>
</svg>

我們把這個圖標簡化下(手工或者通過 SVGOMG 處理),只保留  viewBox 屬性及必要的部分:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15">
  <path d="M7.5,0.5c3.9,0,7,3.1,7,7c0,3.9-3.1,7-7,7c-3.9,0-7-3.1-7-7l0,0C0.5,3.6,3.6,0.5,7.5,0.5 C7.5,0.5,7.5,0.5,7.5,0.5L7.5,0.5L7.5,0.5z M6.1,4.7v5.6l4.2-2.8L6.1,4.7z"/>
</svg>

將 <svg viewBox="…"> 寫成 <symbol id="…" viewBox="…"> 格式,放到 SVG sprite 文件中去。

<svg xmlns="http://www.w3.org/2000/svg">
  <symbol id="cross" viewBox="0 0 20 20">
    <path d="M17.1 5.2l-2.6-2.6-4.6 4.7-4.7-4.7-2.5 2.6 4.7 4.7-4.7 4.7 2.5 2.5 4.7-4.7 4.6 4.7 2.6-2.5-4.7-4.7"/>
  </symbol>
  <symbol id="play" viewBox="0 0 15 15">
    <path d="M7.5,0.5c3.9,0,7,3.1,7,7c0,3.9-3.1,7-7,7c-3.9,0-7-3.1-7-7l0,0C0.5,3.6,3.6,0.5,7.5,0.5 C7.5,0.5,7.5,0.5,7.5,0.5L7.5,0.5L7.5,0.5z M6.1,4.7v5.6l4.2-2.8L6.1,4.7z"/>
  </symbol>
</svg>

你可以選擇手動處理所有圖標,也可以選擇使用工具處理,我們用了 gulp-svg-sprite 插件(附上我們的 gulpfile 配置示例 ),還有很多圖形工具和命令行工具可以導出 SVG sprite 文件,比如 Icomoon

建議:icon文件放到同一文件夾統一管理

如果你手動創建 SVG sprite,我建議為 SVG 圖標專門開一個文件夾。

assets/
    icons/
        cross.svg
        play.svg
        search.svg
        ...
public/
    sprite/
        icons.svg

當你需要重新構建 icons.svg 或者修改某個圖標時,你仍然可以找到圖標的源文件(在 icons 文件夾中)。請盡量保持 SVG sprite 文件與源文件同步。當然如果你有 Grunt/Gulp 做自動構建打包時,你只需要維護一份圖標源文件(即 icons 文件夾)。

第三步:將圖標放到網頁中

為了使用 SVG 圖標,我們得把它放到 HTML 中去,我們不能用 CSS 的 background 相關屬性,不能使用 ::before 等偽元素。用法如下:

<svg><use xlink:href="/path/to/icons.svg#play"></use></svg>

為圖標提供替代文本

目前有幾種方案可以為圖標添加替代文本(為方便視障用戶),下面將介紹我們所用的方案,這個方案已經經過我們自己的屏幕語音閱讀測試了。

首先,當不需要增加替代文本時(網頁上經常已經存在相關文字內容了),可以設置 aria-hidden="true" 來確保屏幕語音閱讀時會跳過圖標:

<a href="/news/">
  <svg aria-hidden="true">
    <use xlink:href="/path/to/icons.svg#newspaper"></use>
  </svg>
  Latest News
</a>

其次,當遇到 a 標簽或者 button 的內容是圖標時,我們會在上面設置 aria-label 。

<a href="/news/" aria-label="Latest News">
  <svg aria-hidden="true">
    <use xlink:href="/path/to/icons.svg#newspaper"></use>
  </svg>
</a>

還有一種選擇是使用 <title> 標簽,尤其是標簽相互作用時導致 aria-label 失效。舉個例子,當你在 table 中使用 yes/no 標記,你可以這樣:

<tr>
  <svg>
    <title>Yes</title>
    <use xlink:href="/path/to/icons.svg#tick"></use>
  </svg>
</tr>

最后,切記:

  • 替代文本因根據內容來定(比如放大鏡圖標的替代文本為“顯示搜索框“或者”提交搜索“);
  • 替代文本要做國際化。

替代文本應該根據 HTML 內容的上下文而定,有人推薦在 SVG sprite 里面增加 <title> 標簽,但是我們實踐后發現并不總是生效,很多屏幕語音閱讀都會忽略。

外部 sprite 和內聯 sprite

目前為止我們所提到的都是外部的 sprite,但是老版本的 WebKit 內核瀏覽器和所有版本的 IE 瀏覽器(低于 Edge 13),只支持 <use xlink:href="#some-id"/> 這種內聯的引用。可以考慮引入比如 svg4everybody , svgxuse 等來 ployfill,或者將 SVG sprite 元素寫到每個頁面的 HTML 中去。

<body>
  <!-- Hidden icon data -->
  <svg aria-hidden="true" style="display:none">
    <symbol id="icon-play">…</symbol>
    <symbol id="icon-cross">…</symbol>
    <symbol id="icon-search">…</symbol>
  </svg>

  <!-- A visible icon: -->
  <button aria-label="Start playback">
    <svg aria-hidden="true"><use xlink:href="#icon-play"/></svg>
  </button>
</body>

兩種方法各有利弊,比較如下:

  內聯 SVG sprite 外部 SVG sprite
瀏覽器兼容性 原生支持兼容到 IE9+
老版本 Safari/WebKit 中將 SVG sprite置于<body>元素內最頂部
原生支持兼容到 Edge 13+, Safari 9+
IE 和老版本 Safari/WebKit 需要引入JS polyfill 比如 svg4everybodysvgxuse
無法跨域加載外部的 SVG sprite (即使是CORS Chromium bug ),可以通過引入 svgxuse 解決
緩存 無法緩存
每個頁面都會將 SVG sprite重新加載一遍
SVG sprite 是個獨立文件且能被瀏覽器緩存
同時不會增加服務端 HTTP 緩存的體積
渲染速度 SVG sprite 是個獨立文件且能被瀏覽器緩存
同時不會增加服務端 HTTP 緩存的體積
毫無影響
圖標加載可能有延時因為
1)獨立 http 請求圖標
2)瀏覽器不會優先加載它 (詳見 瀏覽器預加載器)。
更新 圖標加載快
僅在弱網情況下,網頁內容因頂部較大的 SVG sprite 的加載而變慢
只有一個靜態文件需要修改,因此對于內聯 SVG sprtie的更新管理和服務端緩存都更容易做。

我喜歡將兩種方法混起來用,創建兩個 SVG sprite:

 

  1. 一個小一點的 SVG sprite 包含常用的圖標,作為內聯元素放到每個頁面中,大小 5KB 以內。
  2. 一個大一點的 SVG sprite 包含全部的圖標,作為外部靜態資源,大小 50KB 以內。

 

在大一點的項目中,我們可以將圖標分組打包成多個 SVG sprite ,服務于網站的某一部分或者某一特定功能。

在 Webpack 中使用 SVG 圖標

譯注:本部分內容與原文無關,是譯者為展示在 Webpack 中使用 SVG icon 的示例。

在日常開發中,我不知道各位前端朋友們有沒有碰到一個問題,就是使用 font-awesome 或類似的 icon-font 包,無法滿足設計稿中的需求,比如說來了一個中國地圖形狀的 icon,你會怎么辦?

如果專門為項目維護一份 icon-font 的話,可能需要設計師每次幫你做一份字體文件,每次增加圖標就要去找設計師幫忙,然后再打包生成 ttf 、 woff 、 woff2 、 eot 一堆文件,至少對于我來說是這樣的。

此外,目前而言,Vue.js 和 React.js 的兼容性都是 IE9 +,所以如果不用管IE 9以下的兼容性,果斷用 SVG sprite 來做圖標啊。

示例將分別介紹 Vue.js 和 React.js 中的用法,工具使用了 svg-sprite-loader 。

demo地址

圖標統一放在 assets/icons 文件夾目錄下:

  assets/
    icons/
      cross.svg
      play.svg
      heart1.svg
      ...
    icon-set.js

在 icon-set.js 中 export 所有圖標:

  import Check from './icons/check.svg'
  import Cross from './icons/cross.svg'
  import Heart1 from './icons/heart1.svg'
  ...

  export {
    Check,
    Cross,
    Heart1,
    ...
  }

webpack 配置中增加 svg-sprite-loader :

  module.exports = {
    ...
    {
      test: /\.svg$/,
      loader: 'svg-sprite',
      include: /assets\/icons/
    },
    ...
  }

此時的 svg 文件通過 svg-sprite-loader 處理,會在 <body> 下插入一個內聯的 SVG sprite:

  <body>
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0;visibility:hidden">
      <defs>
        <symbol viewBox="0 0 20 20" id="check">
          <path d="M10 1c-4.962 0-9 4.038-9 9 0 4.963 4.038 9 9 9 4.963 0 9-4.037 9-9 0-4.962-4.037-9-9-9zm0 16.615c-4.2 0-7.615-3.416-7.615-7.615C2.385 5.8 5.8 2.385 10 2.385c4.2 0 7.615 3.416 7.615 7.615 0 4.2-3.416 7.615-7.615 7.615z" fill="currentColor"></path>
          <path d="M13.664 6.74l-5.05 5.05-2.278-2.28c-.27-.27-.71-.27-.98 0s-.27.71 0 .98l2.77 2.77c.135.134.312.202.49.202.177 0 .354-.068.49-.203l5.537-5.54c.27-.27.27-.708 0-.98-.27-.27-.708-.27-.98 0z"></path>
        </symbol>
        <symbol viewBox="0 0 20 20" id="cross">
          <path d="M19 4.23L15.75 1 10 6.83 4.12 1 1 4.23l5.88 5.83L1 15.9 4.13 19 10 13.17 15.75 19 19 15.9l-5.88-5.84"></path>
        </symbol>
        <symbol viewBox="0 0 20 20" id="heart1">
          <path d="M18.98 5.7c-.24-2.36-2.24-4.2-4.66-4.2-1.95 0-3.6 1.18-4.32 2.87-.7-1.7-2.37-2.87-4.32-2.87-2.42 0-4.42 1.84-4.66 4.2L1 6.18c0 5.7 6.98 8.38 9 12.17 2.02-3.8 9-6.48 9-12.17 0-.16 0-.32-.02-.48z"></path>
        </symbol>
        ...
      </defs>
    </svg>
    ...
  </body>

而 import Check from './icons/check.svg' 中 Check 就是對應的 <symbol> 的 id。

Vue 中 SVG icon 用法示例:

  <template>
    ...
    <svg class="Icon" aria-hidden="true">
      <use :xlink:href="iconSet.Check"></use>
    </svg>
    ...
  </template>

  <script>
  import * as iconSet from '../assets/icon-set'

  export default {
    data () {
      return {
        ...
        iconSet: iconSet
        ...
      }
    }
  }
  </script>

React 中 SVG icon 用法示例:

  import React from 'react';
  import * as iconSet from '../assets/icon-set'

  export default class App extends React.Component {

    render() {
      return (
        <div>
          <svg className="Icon" aria-hidden="true">
            <use xlinkHref={iconSet.Check}></use>
          </svg>
          ...
        </div>
      );
    }
  }

第四步:用 CSS 給圖標增加樣式

我們已經花了大量時間在講 SVG 圖標和 SVG sprite 的制作,如何將圖標放到網頁中,接下來將介紹如何通過 CSS 給圖標增加樣式。

增加 class 名

我們可以在 CSS 通過元素選擇器選中所有的 <svg> 標簽,但如果 SVG 有圖標以外的用途的話,就會出問題,此外FireFox 瀏覽器還存在相關的 bug (),所以最好不要這么做。

而我建議給每個圖標增加兩個 class 名,一個通用型的 class 如 Icon ,一個獨有的 class 如 Icon--arrow 。

<svg class="Icon Icon--arrow" aria-hidden="true">
  <use xlink:href="/path/to/icons.svg#arrow"></use>
</svg>

我們推薦使用 SUIT CSS 命名規則 (你可以選擇喜歡的命名風格),類似 class="icon-arrow" 這種,這樣就可以使用類似 svg[class*="icon-"] 的CSS選擇器選中圖標。

圖標的默認樣式

推薦的默認樣式如下:

.Icon {
  /* 通過設置 font-size 來改變圖標大小 */
  width: 1em; height: 1em;
  /* 圖標和文字相鄰時,垂直對齊 */
  vertical-align: -0.15em;
  /* 通過設置 color 來改變 SVG 的顏色/fill */
  fill: currentColor;
  /* path 和 stroke 溢出 viewBox 部分在 IE 下會顯示 normalize.css 中也包含這行 */
  overflow: hidden;
}

上下兩行圖標都用了默認樣式,差別在于父元素的字體和顏色。

當需要定制某個圖標的樣式時,可以參考下面這段代碼:

.MyComponent-button .Icon {
  /* 設置寬高 */
  font-size: 40px;
  /* 設置顏色 */
  color: purple;
  /* 可能需要重置垂直對齊 */
  vertical-align: top;
}

圖標的顏色與父元素的文本顏色相同,如果圖標沒有繼承父元素的文本顏色( currentColor ),去看看圖標源碼中是否存在 fill 屬性。

SVG 繼承的樣式

SVG許多樣式屬性都是繼承來的,比如在最外層的 <svg> 標簽,通過CSS設置了 fill 屬性,內層的 <path> 、 <circle> 等元素都會繼承該屬性,我們還可以在 <svg> 標簽設置其他CSS屬性,比如 stroke :

.Icon--goldstar {
  fill: gold;
  stroke: coral;
  stroke-width: 5%;
  stroke-linejoin: round;
}

默認樣式和定制樣式的星形圖標

大多情況下不需要修改太多,只要設置 fill 屬性里改變圖標的顏色,有時可能會增加或調整下 stroke 來加個邊框什么的。

雙色圖標

當一個圖標包含兩個 <path> 時就可以設置兩種不同的 fill 值,即顯示兩種顏色。

<symbol id="check" viewBox="0 0 20 20">
  <!-- 繼承 CSS 中設置的 fill 值 -->
  <path d="…" />
  <!-- 繼承 CSS 中設置的 color 值-->
  <path fill="currentColor" d="…" />
</symbol>
.Icon--twoColors {
  fill: rebeccapurple;
  color: mediumturquoise;
}

雙色圖標

留點空間給 stroke

還記得前面提到的在圖標四周留白嗎?在用 stroke 時就顯得尤其重要了。

.Icon--strokespace {
  fill: none;
  stroke: currentColor;
  stroke-width: 5%;
}

在 SVG 中,stroke 在 path 兩側,如果 path 到了 viewport 的邊界,stroke 就會有一半被截斷。

這個例子里,第一個圖標四周并未留白,第二個四周有 0.5px 的留白(viewport 為 15px)

設置 stroke-width 為百分比值

如何設置 stroke 的尺寸是個難題,下面這個例子是兩個 stroke-width 為 1px 的圖標:

stroke-width 的值是跟圖標的尺寸有關,在上圖中:

  1. 第一個圖標的 viewBox 的寬高為20px,所以1px 的 stroke 是圖標尺寸的 1/20,粗細適中。
  2. 第二個圖標的 viewBox 的寬高為500px,所以1px 的 stroke 是圖標尺寸的 1/500,顯得很細。

如果所有圖標的 viewBox 值相同的話,倒不會有什么問題。一旦它們的寬高差別很大時,使用像素單位或者無量綱量單位( stroke-width:1 )就會出問題了。怎么解決這個問題?

推薦使用百分比,同樣的例子,這回設置 stroke-width:5% :

對于正方形圖標,設置 stroke-width: N% 看起來完美解決問題(注意:在太寬或太高的圖標上可能會不太一樣)。

并非所有的 SVG 都是圖標

有些 SVG 并非圖標,就不用放到 SVG sprite 中,比如說:

  • 不需要修改樣式的 SVG 圖形,直接用在 <img> 標簽里就好了。
  • 需要增加動畫的 SVG 圖形,考慮將整個 <svg> 標簽作為內聯元素放入頁面中,這樣就可以選擇特定的部分或 <path> 增加樣式和動畫了。

注意:如果一個 SVG 圖形大小超過100 x100 ,或者內部有很多元素,就不要把它當做圖標來處理了。

第五步:進階技巧

看完前面幾個部分,你已經掌握 SVG 圖標的很多技巧了,接下來是一些的拓展內容。

不要用無樣式的巨型圖標

當樣式文件由于網絡問題加載失敗時,網頁就失去了樣式,如果網頁內容結構化良好,頁面內容仍然可讀,但是圖標就會顯示成一個龐然大物了。

最新的瀏覽器默認將 SVG 元素顯示成 300x150 px,其他瀏覽器可能會把 width 設置成100%

建議把樣式直接寫到 <head> 標簽里面。

<style>.Icon{width:1em;height:1em}</style>

預加載外部的 SVG sprite

在第三部分中,我們提過外部 SVG sprite 可以延遲加載,因為瀏覽器預加載模塊不會識別處理 <use xlink:href="/path/to/icons.svg#something"></use> 這種形式。

那我們可以做點什么:

  • 標準且前衛的方法:在 <head> 里加一個 <link rel="preload" href="/path/to/icons.svg" as="image"> ( 有關預加載的細節 :支持最新 Chrome,其他瀏覽器即將支持)。
  • 保守的方法:在 <body> 里最前面的位置加上 <img style="display:none" alt="" src="/path/to/icons.svg"> 。

我沒有實際測試這些方案,通常來說把內聯和外部 SVG sprite 并用,就已經有足夠的性能表現,已不太需要再去關心預加載,但是偶爾探索下相關知識也不失為一件好事。

選中獨立的 path

我們已經學習了定制 symbol 中所有路徑的 fill 、 stroke ,為 path 增加多種顏色。但是可以直接選中特定的 path (使用 class 選擇器)繼而修改樣式嗎?

答案是:可以,也不可以。

  1. 如果使用了外部 SVG sprite,無法選中 <symbol> 里的任何 <path> 和其他元素。
  2. 如果使用了內聯 SVG sprite,可以選中 <path> 并修改樣式,但是所有地方都應用這些樣式。

所以,即使是內聯 SVG sprite,可以這么寫:

#my-symbol .style1 {
  /* Styles for one group of paths */
}
#my-symbol .style2 {
  /* Styles for another */
}

不可以這么寫:

.MyComponent-button .Icon .style1 {
  /* For 1 group of paths for this icon in this context */
}
.MyComponent-button .Icon .style2 {
  /* For another group */
}

譯者注: .Icon 與 .style1 并非嵌套關系, .MyComponent-button .Icon .style1 無法選中 class 名為 style1 的 path 。

除非在火狐瀏覽器離,你可以輕松選中 <symbol> 中的實例,但這是屬于火狐瀏覽器的私有特性,意味著其他瀏覽器存在著兼容性問題,所以希望火狐瀏覽器能修復這個問題(或者說是 bug)。當新標準來臨時,也許可以通過 Shadow DOM 來選中,但標準本身也在不斷變化,所以這一點無法確定( /deep/ 連結符 已被棄用)。

通過 CSS 變量繪制兩種以上顏色

到目前為止,我們學習了通過 CSS 繪制單色和雙色 SVG 圖標,那有沒有可能繪制三種、四種甚至更多顏色呢?我們可以通過 CSS 變量 (又稱 CSS custom properties)實現,這需要在 SVG 上寫不少東西。

<symbol id="iconic-aperture" viewBox="0 0 128 128">
  <path fill="var(--icon-color1)" d="…" />
  <path fill="var(--icon-color2)" d="…" />
  <path fill="var(--icon-color3)" d="…" />
  <path fill="var(--icon-color4)" d="…" />
  <path fill="var(--icon-color5)" d="…" />
  <path fill="var(--icon-color6)" d="…" />
</symbol>

下面這個 demo 中,是從 Iconic 里“偷”來了一個圖標,嘗試模仿下 Iconic 中圖標多色效果。

一個 symbol 中使用了 6個不同的 CSS 變量(在支持 CSS 變量的 Firefox 、Chrome 或者 Safari 9.1+ 中打開)

上面的例子中只有一個圖標,第一個圖標沒有聲明變量,所以 fallback 成了currentColor,后面兩個圖標每個都聲明過一套顏色變量,記得在支持 CSS 變量 的瀏覽器中打開下實際效果。

stroke-width 的百分比值究竟是怎么計算出來的?

當我們把 stroke-width 設置為 N% 時,這個百分比值是根據什么來定的?是根據圖標的寬度或者高度嗎?根據 官方文檔 ,其實是和對角線有關,1% 的值為:對角線長度除以根號2(接近1.4)后取 1%。

這意味著對于正方形圖標,1% 就等于寬度或高度的 1%,對于較寬或較高的圖標的話結果不太一樣。

第二個圖標的寬高比為2:1,設置了 stroke-width:5% 后,輪廓的寬度約為寬度的7.91%,高度的3.95%。

總體來說,建議把 stroke-width 的值設為百分比。如果你在使用方形的話,那么1%約為圖寬度的百分之一。

不能使用 漸變/gradient

有沒有可能使用 gradient 設置 fill 值呢?事實是不能,CSS的 linear-gradient() 產生的是一個圖片值,而 fill 屬性是不接受圖片值的。

SVG 的編碼及使用 gradient 有其特定語法,但跟咱們討論的主題(SVG 圖標)關系不是很大,可以嘗試下,但是這得花功夫,而且至少得硬編碼進入一些參數,有興趣的話可以嘗試下。

第六步:部分瀏覽器存在的 bug

Safari:避免給 <svg> 設置 width / height 屬性

為了避免出現頁面未加載樣式時,部分圖標顯示巨大的問題,我們給 <svg> 設置 width 、 height 屬性。

<svg width="20" height="20">
  <use xlink:href="…"></use>
</svg>

接著我們在 iOS 的 Safari 測試下,有一半 SVG 圖標掛了?什么鬼?!

實際上,Safari/WebKit 不支持先給 SVG 設置寬高屬性,再通過 CSS 去改變尺寸的,特別是想把圖標變小時,圖標的容器會變小,但圖標的內容并不生效。

我們的解決方案是移除 SVG 上的 width 、 height 屬性,只通過 CSS 控制圖標尺寸。最新的 Safari 已經修復了這個問題(Safari 9.1 桌面版和 iOS 9.3)。

Safari:避免在 <svg> 標簽上設置 padding

如果你想要設置背景色、邊框、內邊距等,你應該往圖標的父元素上增加樣式,而不是圖標自身的 <svg> 標簽,雖然看起來最新的瀏覽器上都沒有問題,但是老版本 WebKit 瀏覽器上渲染存在問題,因此建議在圖標外面包一層,比如 <span> 、 <button> 、 <a> 等。

樣式直接加到 svg 標簽上和加到父元素上。多數瀏覽器的渲染效果相同,但老版本 WebKit 瀏覽器渲染會有點偏差。

Firefox:避免使用 svg 作為元素選擇器

為什么呢?當我們使用 <use> 標簽時,瀏覽器會創建 Shadow DOM 去復制 <symbol> 中的內容,看下來就像這樣:

<svg class="Icon Icon--something" aria-hidden="true">
  <use xlink:href="#something">
    <svg viewBox="0 0 20 20">
      <path d="…" />
    </svg>
  </use>
</svg>

之前的部分提到過,Firefox 瀏覽器目前支持選中 <use> 標簽創建的 Shadow DOM 中的內容,所以如果寫了這樣的 CSS:

svg {
  fill: red;
}
.Icon--something {
  fill: green;
}

在 Firefox 瀏覽器中就會變成類似這樣:

<svg class="Icon Icon--something" aria-hidden="true" fill="green;">
  <use xlink:href="#something">
    <svg viewBox="0 0 20 20" fill="red;">
      <path d="…" />
    </svg>
  </use>
</svg>

圖標在其他瀏覽器中是綠色的,但是在 Firefox 瀏覽器中會是紅色,因為內部的 <svg> 標簽按照 CSS 第一行中的 fill: red 去渲染。

還有一種寫法可以避免:

:not(use) > svg { … }

來自: http://qianduan.guru/2016/04/17/How-to-work-with-SVG-icons/

原文: http://fvsch.com/code/svg-icons/how-to

 本文由用戶 ercjd 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!