如果你還在使用 Icon Font 作為網(wǎng)頁中顯示圖標(biāo)的實(shí)現(xiàn)方案,那么你可能有點(diǎn) Out 了。
由于使用 Icon Font 顯示圖標(biāo)存在一些缺陷,開發(fā)者們一直在致力于探索使用 SVG 作為替代的方法。
這篇文章列舉了目前 SVG 比較常見的使用方法。
關(guān)于使用 Icon Font 的缺陷,這篇來自 CSS Trick 的 《Inline SVG vs Icon Font》
可謂是總結(jié)的相當(dāng)全面了。在我看來,Icon Font 的主要缺陷有以下幾條:
瀏覽器將其視為文字進(jìn)行抗鋸齒優(yōu)化,有時(shí)得到的效果并沒有想象中那么銳利。
尤其是在不同系統(tǒng)下對文字進(jìn)行抗鋸齒的算法不同,可能會導(dǎo)致顯示效果不同。
Icon Font 作為一種字體,Icon 顯示的大小和位置可能要受到font-size
、line-height
、word-spacing
等等 CSS 屬性的影響。
Icon 所在容器的 CSS 樣式可能對 Icon 的位置產(chǎn)生影響,調(diào)整起來很不方便。
使用上存在不便。首先,加載一個(gè)包含數(shù)百圖標(biāo)的 Icon Font,卻只使用其中幾個(gè)圖標(biāo),非常浪費(fèi)加載時(shí)間。
自己制作 Icon Font 以及把多個(gè) Icon Font 中用到的圖標(biāo)整合成一個(gè) Font 也非常不方便。
為了實(shí)現(xiàn)最大程度的瀏覽器支持,可能要提供至少四種不同類型的字體文件。包括TTF
、WOFF
、EOT
以及一個(gè)使用 SVG 格式定義的字體。
開發(fā)者們想出了多種使用 SVG 的技巧來解決/緩解上述問題,下面我們來逐個(gè)盤點(diǎn)目前常見的使用方法。
img/object 標(biāo)簽使用 img 和 object 標(biāo)簽直接引用 SVG 是早期常見的使用方法。
這種方法的缺點(diǎn)主要在于要求每個(gè)圖標(biāo)都單獨(dú)保存成一個(gè) SVG 文件,使用時(shí)也是單獨(dú)請求的。
如果在頁面中使用的多個(gè)圖標(biāo),每個(gè)都是單獨(dú)請求的話會產(chǎn)生很多請求數(shù),增加服務(wù)端的負(fù)載和拖慢頁面加載速度,
因此現(xiàn)在很少使用了。
不過,在 IE 中可以使用 object 標(biāo)簽實(shí)現(xiàn)最后討論的 SVG Defs/Symbols 的效果。
Inline SVG所謂 Inline SVG,就是直接把 SVG 寫入 HTML 中,這種方法簡單直接,而且具有最強(qiáng)的可調(diào)性。
使用這種方法,你可以使用 CSS 的fill
屬性和stroke
屬性來控制填充顏色和邊線的顏色,
如果 SVG 圖標(biāo)包含多個(gè)部分,你甚至可以設(shè)置每個(gè)部分的樣式。同時(shí),使用 JavaScript 修改 SVG 和生成動畫效果都可以實(shí)現(xiàn)。
Inline SVG 作為 HTML 文檔的一部分,不需要單獨(dú)請求。臨時(shí)需要修改某個(gè)圖標(biāo)的形狀也比較方便。
但是 Inline SVG 使用上比較繁瑣,需要在頁面中插入一大塊 SVG 代碼因此不適合手寫,圖標(biāo)復(fù)用起來也比較麻煩。
好在我們大部分的頁面都是由某種模板渲染出來的,無論是使用 PHP、Jinja2 還是 ERuby 模板語言,
都可可以定義一個(gè)函數(shù)來幫我們 include 這些 SVG。因此在很多情況下是很好的解決方案,
其不適合的主要使用場景就是純靜態(tài)頁面或者前后端分離客戶端頁面。
Data URIsData URIs 是一種不怎么常見的技巧。之前我們在 CSS 里定義元素的背景圖片時(shí),常使用像下面這種方式
1 2 3 4 .icon { backgound-image: url(icons/a.png) /* ... */ }
而現(xiàn)在,url
當(dāng)中可以放置的可以不僅僅是指向資源的 URL 鏈接,而可以是數(shù)據(jù)本身。使用 Data URIs,無論是圖片還是 SVG,
你都可以將其編碼為 base64 并直接寫入 CSS。譬如
1 2 3 .icon{ background: url(data:text/svg+xml;base64,<base64 encoded data>) }
關(guān)于 base64 編碼,請參考wiki ,Data URI 的格式定義如下
1 data:[<mime type>][;charset=<charset>][;base64],<encoded data>
使用這種方法,SVG Icon 使用起來和 Icon Font 一樣只需要為元素添加 CSS 即可,所有的資源都可以整合在一個(gè) CSS 文件中,
不需要額外引用 SVG 文件。
如果你在使用 Gulp 或者 Grunt 這樣的 Build 工具,那么將 SVG 整合到一個(gè) CSS
當(dāng)中是可以非常方便地自動化完成的。這個(gè)任務(wù)只有簡單的字符串和編碼處理,基本不需要依賴非 JavaScript 的庫和資源。
使用這種方法的缺點(diǎn)是不能方便地使用 CSS 修改 Icon 的顏色和邊線屬性。
SVG Sprite目前,一些提供制作 Icon Font 功能的網(wǎng)站(如icomoon )已經(jīng)提供輸出 SVG Sprites 功能了。
SVG Sprites 可以看做上述 Data URIs 方法和之前使用位圖的 Sprite 方法的組合。
在 Icon Font 還沒普及、圖標(biāo)還主要依靠位圖顯示的時(shí)候,前端工程師都會使用 Sprite 來減少圖片請求的次數(shù)。
其原理很簡單:將所有的圖標(biāo)以一定的間隔排列起來組成一整張大圖片,使用時(shí)對于某個(gè) 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 通過設(shè)定background-position
調(diào)整大圖片在背景中的位移,只將某個(gè)單個(gè) Icon 暴露出來,其他部分都切掉。
對于所有的 Icon 都寫成這樣的 CSS 即可使用了。基礎(chǔ)的 SVG Sprite 其實(shí)只是將原來的位圖改成了SVG。
SVG Sprite 相比于原來的位圖 Sprite 的一個(gè)優(yōu)點(diǎn)就是可以通過 CSS 調(diào)整 Icon 顯示的大小。
使用時(shí)還可以 Fallback 到位圖的 Sprite,因此有極好的瀏覽器兼容性。
不過和 Data URIs 方法一樣它同樣存在不能方便調(diào)整顏色樣式的問題。
目前輔助生成 SVG Sprites 的工具有 grunt-iconizr 、
gulp-svg-sprites 等。
使用這兩個(gè)工具,只需將用到的 SVG 放到某個(gè)文件夾中就可以自動被拼合成 Sprite 并輸出對應(yīng) CSS。
兩個(gè)工具都支持生成 PNG 格式的位圖作為 Fallback,缺點(diǎn)是生成位圖要依賴phantomjs
這個(gè)重量級 JS 庫。
SVG Defs/SymbolsSVG Defs 和 Symbols 的原理類似,這里著重介紹一下 SVG Symbols 的使用,
SVG Defs/Symbols 本質(zhì)上是對 Sprite 的進(jìn)一步優(yōu)化。之前,我們需要使用相對位置來控制哪個(gè)圖標(biāo)被顯示出來,
但是其實(shí) SVG 本身的定義允許你以某一種方式直接引用 SVG 中的某一部分。
將多個(gè)圖標(biāo)整合成一個(gè) SVG 中的多個(gè) Symbols 之后是下面這樣的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <svg xmlns="http://www./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>
每個(gè) Symbol 設(shè)置一個(gè) id 作為引用時(shí)的名字。使用 id 引用這個(gè) SVG 中的 Icon 有兩種方式。
第一種,將上述 SVG 作為 body 的第一個(gè)元素插入在 HTML 中(Chrome 存在一個(gè)
bug 導(dǎo)致不在這里顯示不出圖像),
此后,在需要顯示某個(gè) Icon 的地方插入下面的代碼即可:
1 2 3 <svg class="icon"> <use xlink:href="#circle-cross"></use> </svg>
這里的use
標(biāo)簽直接使用#circle-cross
這個(gè) id 引用了 SVG 中的圖標(biāo)。這種方式的瀏覽器兼容性較好。
我更喜歡的是第二種方式,這種方式不需要在 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>
顯示出來的效果就是下面這個(gè)樣子(可以使用瀏覽器的 Debug 工具來檢視下面的代碼)。
這種方式使用上跟img
標(biāo)簽沒有什么太大的差別了。好處在于所有的圖標(biāo)都在一個(gè)文件中,因此只會請求一次。
這種不需要像 Sprite 那樣繁瑣的設(shè)置圖片的位移。使用 id 命名圖標(biāo)并使用時(shí)直接使用 id 引用既直觀又簡單。
其靈活性和 Inline SVG 幾乎一樣——你可以設(shè)置顏色、邊線樣式、大小等等。
視瀏覽器的不同,有時(shí)你需要使用作為 SVG 標(biāo)簽的開始。
1 <svg xmlns="http://www./2000/svg">
對于 IE 則需要使用object
標(biāo)簽代替<svg><use>
。關(guān)于兼容性討論詳見
這篇文章 。
除了前面提到過的幾個(gè) Gulp 和 Grunt 的插件都已經(jīng)支持 SVG Defs/Symbols 了之外,這里再推薦一個(gè)更輕量的 Gulp 插件
gulp-svg-symbols 。如果只使用 SVG Symbols 而無需 Sprite 支持,
那么使用 gulp-svg-symbols 可以免去對 phantomjs
的依賴。
結(jié)語SVG Icons 作為一種 Icon Font 的替代,使用上具有靈活性好、顯示效果好、可控性強(qiáng)等諸多優(yōu)點(diǎn)。
尤其是最后介紹的 SVG Defs/Symbols 這種方式,已經(jīng)讓 SVG Icon 變成了一種實(shí)用的解決方案。
瀏覽器兼容性是前端領(lǐng)域永恒的話題,目前來看對 SVG 的支持情況確實(shí)沒有 Web Font 那么好,
其中,主流移動瀏覽器(Safari, Chrome, IE for Windows Phone)都已經(jīng)基本兼容 SVG,
但是桌面領(lǐng)域中仍然要面對 IE8 以下的瀏覽器,此外對 SVG Defs/Symbols 的支持也還存在差異。
現(xiàn)階段,如果對瀏覽器兼容性要求比較嚴(yán)苛(主要是支持 IE6-8),則可以權(quán)衡考慮 Icon Font 和帶 Fallback 的 SVG Sprite。
否則的話,推薦使用 SVG Defs/Symbols 的方式代替 Icon Font。
個(gè)人認(rèn)為 SVG Icons 已經(jīng)具備普及的條件了。