若是你還在使用 Icon Font 做爲網頁中顯示圖標的實現方案,那麼你可能有點 Out 了。 因爲使用 Icon Font 顯示圖標存在一些缺陷,開發者們一直在致力於探索使用 SVG 做爲替代的方法。 這篇文章列舉了目前 SVG 比較常見的使用方法。css
關於使用 Icon Font 的缺陷,這篇來自 CSS Trick 的 《Inline SVG vs Icon Font》 可謂是總結的至關全面了。在我看來,Icon Font 的主要缺陷有如下幾條:前端
font-size
、line-height
、word-spacing
等等 CSS 屬性的影響。 Icon 所在容器的 CSS 樣式可能對 Icon 的位置產生影響,調整起來很不方便。TTF
、WOFF
、EOT
以及一個使用 SVG 格式定義的字體。開發者們想出了多種使用 SVG 的技巧來解決 / 緩解上述問題,下面咱們來逐個盤點目前常見的使用方法。git
使用 img 和 object 標籤直接引用 SVG 是早期常見的使用方法。 這種方法的缺點主要在於要求每一個圖標都單獨保存成一個 SVG 文件,使用時也是單獨請求的。 若是在頁面中使用的多個圖標,每一個都是單獨請求的話會產生不少請求數,增長服務端的負載和拖慢頁面加載速度, 所以如今不多使用了。github
不過,在 IE 中可使用 object 標籤實現最後討論的 SVG Defs/Symbols 的效果。算法
所謂 Inline SVG,就是直接把 SVG 寫入 HTML 中,這種方法簡單直接,並且具備最強的可調性。 使用這種方法,你可使用 CSS 的fill
屬性和stroke
屬性來控制填充顏色和邊線的顏色, 若是 SVG 圖標包含多個部分,你甚至能夠設置每一個部分的樣式。同時,使用 JavaScript 修改 SVG 和生成動畫效果均可以實現。gulp
Inline SVG 做爲 HTML 文檔的一部分,不須要單獨請求。臨時須要修改某個圖標的形狀也比較方便。 可是 Inline SVG 使用上比較繁瑣,須要在頁面中插入一大塊 SVG 代碼所以不適合手寫,圖標複用起來也比較麻煩。後端
好在咱們大部分的頁面都是由某種模板渲染出來的,不管是使用 PHP、Jinja2 仍是 ERuby 模板語言, 均可能夠定義一個函數來幫咱們 include 這些 SVG。所以在不少狀況下是很好的解決方案, 其不適合的主要使用場景就是純靜態頁面或者先後端分離客戶端頁面。瀏覽器
Data URIs 是一種不怎麼常見的技巧。以前咱們在 CSS 裏定義元素的背景圖片時,常使用像下面這種方式前端工程師
1 2 3 4 |
.icon { backgound-image: url(icons/a.png) /* ... */ } |
而如今,url
當中能夠放置的能夠不只僅是指向資源的 URL 連接,而能夠是數據自己。使用 Data URIs,不管是圖片仍是 SVG, 你均可以將其編碼爲 base64 並直接寫入 CSS。譬如前後端分離
1 2 3 |
.icon{ background: url(data:text/svg+xml;base64,<base64 encoded data>) } |
關於 base64 編碼,請參考wiki,Data URI 的格式定義以下
1 |
data:[<mime type>][;charset=<charset>][;base64],<encoded data> |
使用這種方法,SVG Icon 使用起來和 Icon Font 同樣只須要爲元素添加 CSS 便可,全部的資源均可以整合在一個 CSS 文件中, 不須要額外引用 SVG 文件。 若是你在使用 Gulp 或者 Grunt 這樣的 Build 工具,那麼將 SVG 整合到一個 CSS 當中是能夠很是方便地自動化完成的。這個任務只有簡單的字符串和編碼處理,基本不須要依賴非 JavaScript 的庫和資源。
使用這種方法的缺點是不能方便地使用 CSS 修改 Icon 的顏色和邊線屬性。
目前,一些提供製做 Icon Font 功能的網站 (如icomoon) 已經提供輸出 SVG Sprites 功能了。 SVG Sprites 能夠看作上述 Data URIs 方法和以前使用位圖的 Sprite 方法的組合。
在 Icon Font 還沒普及、圖標還主要依靠位圖顯示的時候,前端工程師都會使用 Sprite 來減小圖片請求的次數。 其原理很簡單:將全部的圖標以必定的間隔排列起來組成一整張大圖片,使用時對於某個 Icon,編寫以下所示的 CSS。
1 2 3 4 5 6 |
.icon-a { background-image: url(/path/to/pic/contains/all/icons.png); background-position: 0 120px !important; width: 24px; height: 24px; } |
上述 CSS 經過設定background-position
調整大圖片在背景中的位移,只將某個單個 Icon 暴露出來,其餘部分都切掉。 對於全部的 Icon 都寫成這樣的 CSS 便可使用了。基礎的 SVG Sprite 其實只是將原來的位圖改爲了 SVG。
SVG Sprite 相比於原來的位圖 Sprite 的一個優勢就是能夠經過 CSS 調整 Icon 顯示的大小。 使用時還能夠 Fallback 到位圖的 Sprite,所以有極好的瀏覽器兼容性。 不過和 Data URIs 方法同樣它一樣存在不能方便調整顏色樣式的問題。
目前輔助生成 SVG Sprites 的工具備 grunt-iconizr、 gulp-svg-sprites 等。 使用這兩個工具,只需將用到的 SVG 放到某個文件夾中就能夠自動被拼合成 Sprite 並輸出對應 CSS。 兩個工具都支持生成 PNG 格式的位圖做爲 Fallback,缺點是生成位圖要依賴phantomjs這個重量級 JS 庫。
SVG Defs 和 Symbols 的原理相似,這裏着重介紹一下 SVG Symbols 的使用, SVG Defs/Symbols 本質上是對 Sprite 的進一步優化。以前,咱們須要使用相對位置來控制哪一個圖標被顯示出來, 可是其實 SVG 自己的定義容許你以某一種方式直接引用 SVG 中的某一部分。
將多個圖標整合成一個 SVG 中的多個 Symbols 以後是下面這樣的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<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 做爲引用時的名字。使用 id 引用這個 SVG 中的 Icon 有兩種方式。
第一種,將上述 SVG 做爲 body 的第一個元素插入在 HTML 中 (Chrome 存在一個 bug 致使不在這裏顯示不出圖像), 此後,在須要顯示某個 Icon 的地方插入下面的代碼便可:
1 2 3 |
<svg class="icon"> <use xlink:href="#circle-cross"></use> </svg> |
這裏的use
標籤直接使用#circle-cross
這個 id 引用了 SVG 中的圖標。這種方式的瀏覽器兼容性較好。
我更喜歡的是第二種方式,這種方式不須要在 body 開始的地方插入 SVG,而是使用完整路徑引用 Icon。 也就是:
1 2 3 4 5 6 |
<svg class="icon"> <use xlink:href="/img/posts/svg-icons.svg#circle-check"></use> </svg> <svg class="icon"> <use xlink:href="/img/posts/svg-icons.svg#circle-cross"></use> </svg> |
顯示出來的效果就是下面這個樣子 (可使用瀏覽器的 Debug 工具來檢視下面的代碼)。
這種方式使用上跟img
標籤沒有什麼太大的差異了。好處在於全部的圖標都在一個文件中,所以只會請求一次。 這種不須要像 Sprite 那樣繁瑣的設置圖片的位移。使用 id 命名圖標並使用時直接使用 id 引用既直觀又簡單。 其靈活性和 Inline SVG 幾乎同樣——你能夠設置顏色、邊線樣式、大小等等。 視瀏覽器的不一樣,有時你須要使用做爲 SVG 標籤的開始。
1 |
<svg xmlns="http://www.w3.org/2000/svg"> |
對於 IE 則須要使用object
標籤代替<svg><use>
。關於兼容性討論詳見 這篇文章。
除了前面提到過的幾個 Gulp 和 Grunt 的插件都已經支持 SVG Defs/Symbols 了以外,這裏再推薦一個更輕量的 Gulp 插件 gulp-svg-symbols。若是隻使用 SVG Symbols 而無需 Sprite 支持, 那麼使用 gulp-svg-symbols 能夠免去對 phantomjs 的依賴。
SVG Icons 做爲一種 Icon Font 的替代,使用上具備靈活性好、顯示效果好、可控性強等諸多優勢。 尤爲是最後介紹的 SVG Defs/Symbols 這種方式,已經讓 SVG Icon 變成了一種實用的解決方案。
瀏覽器兼容性是前端領域永恆的話題,目前來看對 SVG 的支持狀況確實沒有 Web Font 那麼好, 其中,主流移動瀏覽器 (Safari, Chrome, IE for Windows Phone) 都已經基本兼容 SVG, 可是桌面領域中仍然要面對 IE8 如下的瀏覽器,此外對 SVG Defs/Symbols 的支持也還存在差別。
現階段,若是對瀏覽器兼容性要求比較嚴苛 (主要是支持 IE6-8),則能夠權衡考慮 Icon Font 和帶 Fallback 的 SVG Sprite。 不然的話,推薦使用 SVG Defs/Symbols 的方式代替 Icon Font。
我的認爲 SVG Icons 已經具有普及的條件了。