從 CSS sprite 進化到 SVG sprite

很久沒寫文章了,今天想跟大家分享 CSS sprite、Web icon font,以及 SVG sprite 這幾個東西的使用經驗。

這三個東西,雖然看似毫無關聯,但隱隱約約還是有種「進化」的感覺,如果你還在使用 CSS sprite 的技術,歡迎參考一下這篇文章,試著玩玩 Icon font 或 SVG sprite,瞭解並感受一下他們的特別之處吧 😄

CSS Sprite

當我們瞭解到每讀取一張圖,就會佔用一個 HTTP 需求時,我們改用 CSS sprite。
我們將所有網站會用到的 icon,全部集合起來放在同一張圖片裡,利用 class 再搭配 background-position 幫圖片做定位,就做到了 CSS sprite 的技術啦。

雖然可以有效減少 HTTP 需求,但他終究是圖片,只要把網站等比放大,圖片就可能會失真。在現今螢幕解析度提高,以及 RWD 網站盛行的環境下,用 CSS sprite 要面對的可能就是這種失真的小瑕疵。

另外一個比較惱人的困擾,就是要把所有的 icon 排在同一張圖片,再自己算出定位,其實很麻煩。雖然可以使用 compass 這樣的 CSS 預處理器幫忙,但也不是所有人都用 compass 啊 😊。


Web Icon Font

因為種種的因素,我們改用了另外一個技術:Web Icon Font (以下簡稱 icon font),我們利用一些字體文件,將文字轉成圖片。好處是解決了「失真」的困擾,因為即使放大縮小,它的本質還是文字,文字放大縮小是不會失真的,可以保持它原有的清晰度。

但困擾來了,正因為他是文字,所以必須乖乖遵守文字的規範,我們常碰到的問題就是在「鋸齒」與「抗拒齒」之間徘徊,通常瀏覽器都會替文字進行抗拒齒的優化,以免放大閱讀文字時,會有凹凸不平的鋸齒狀,有的時候,某些圖標我們需要這些鋸齒,但另外一些圖片我們需要抗鋸齒,線段類的圖案、圓弧狀的圖案 … 等等,會依照不同的樣式有不同的需求。

而且除了瀏覽器外,不同系統 (WIN, iOS)針對文字的抗拒齒演算法也不同,如果真的要錙銖必較的去調校,應該會調整的天荒地老。

當然,這篇文章不是要一味地告訴大家 Icon Font 的缺點,畢竟我自己也還在用 Icon Font XDD。

這種細微的的改變,一般肉眼看不太出來,而且在解析度較高的裝置觀看,這問題才會比較明顯。如果真的發生類似的問題,我們可以用font-smooth去調整:

font-smooth: auto | never | always | | length | initial | inherit
-webkit-font-smoothing : none | subpixel-antialiased | antialiased
font-smooth | MDN

.icon {
  font-smooth: always;
  -webkit-font-smoothing: antialiased;
}

但誠如 MDN 說的: this is non-standard and should not be used. (他們不是標準格式,不推薦使用在專案上)。

因此,如果真的有這類問題需要解決,我們可以改使用 SVG sprite。

Icon font 還有其他不方便的地方,例如受到font-size, line-height..等等字型屬性的限制。一次要載入上百張 Icon font 的字型檔,但實際上用到的只是那幾張小圖,浪費了讀取的時間。最後還有一點是各家的 Icon font 整合不易.. 等。

當然還是有一些網站服務可以解決部分問題,所以這邊只是把會碰到的狀況列出來,就不再細談了。


SVG sprite

在介紹 Icon font 文末提到有「網站服務」可解決部分問題,而這個網站也是我要介紹給大家的,就是 IcoMoon,他是一個整合眾家 Icon font 的網站,此外也支援 SVG 匯出。

該如何使用它?這邊不再贅述,我們只談 SVG sprite 的功能。

怎麼使用 SVG

在講 SVG sprite 之前,還是先簡單講一下什麼是 SVG 吧 XDDD。其實對我而言,SVG 的定義很簡單,就是用程式碼畫圖,這句話傳達了兩個意思:一、你必須要寫程式碼;二、他是不會失真的向量圖。

See the Pen ZYKqea by MUKi (@mukiwu) on CodePen.

透過以上範例,你會發現這張圖片所有的線段(<path>)都是寫在 HTML 裡面,然後再利用 CSS 屬性去修改它的顏色、大小… 等樣式。

每一張圖片都有一個自己的<path>或者<circle> … 這樣的屬性,通常會寫在網頁的最上方,再利用<use xlink:href="#icon-video-camera"></use>呼叫他。

雖然使用 SVG 可以有效解決解析度、鋸齒 … 等等這些問題,但參考我們上述語法,應該會發現,如果要載入多張不同的圖片,程式碼會拖得很長。為了解決這個問題,我們可以改用 SVG sprite,也就是將所有圖片存在 SVG 檔案,再從 HTML 呼叫他。

SVG sprite 實作

以 IcoMoon 網站為例,先選好你要的圖片,在匯出 SVG 之前勾選 Include Tiles (CSS Sprite)

解壓縮之後,找到sprite/目錄,裡面有一個sprite.svg,原理就跟 sprite.png 一樣,會把所有圖案排列整齊,差別在於它是向量圖,放大縮小都不會失真。

接下來只要在 HTML 載入這張 SVG 檔案,並且透過ID去呼叫對應的 icon 就可以了。

<svg class="icon" xmlns="http://www.w3.org/2000/svg">
	<use xlink:href="sprite.svg#icon-facebook"></use>
</svg>
<svg class="icon" xmlns="http://www.w3.org/2000/svg">
	<use xlink:href="sprite.svg#icon-feed"></use>
</svg>

xlink:href的地方,就輸入這個 svg 檔案的相對路徑,後面再寫 ID 即可,跟 CSS sprite 相比是不是簡單很多呢。我們不需要再寫background-position去調整他的位子,往後要新增其他的 icon,直接打開 sprite.svg 編輯就可以啦!

因為 svg 目前不支援絕對路徑,所以我沒辦法直接放在 codepen 讓大家看,因此另外做了一份頁面,請參考 🌍 範例網頁觀看效果,也歡迎使用 dev tools 玩玩看。

➡️ 範例:SVG sprite HTML demo

使用 IcoMoon 的 SVG sprite 注意事項

因為 IcoMoon 可以直接提供我們 SVG 的 path 程式碼,而且可以個別匯出,所以我用 IcoMoon 舉例。但如果你以後想用 IcoMoon 做 SVG sprite,請特別注意一件事情,就是你可能需要自行修改它的 SVG 檔案。

什麼意思呢?因為 IcoMoon 把 icon 的顏色、大小都寫死在 SVG 檔案裡,而且我覺得不是很好管理。

icon-facebook為例,IcoMoon 是這樣寫的:

<svg id="icon-facebook" width="32" height="32" viewBox="0 0 32 32" x="48" y="0">
<path fill="#444444" d="M26.667 0h-21.334c-2.945 0-5.333 2.388-5.333 5.334v21.332c0 2.946 2.387 5.334 5.333 5.334h10.667v-14h-4v-4h4v-3c0-2.761 2.239-5 5-5h5v4h-5c-0.552 0-1 0.448-1 1v3h5.5l-1 4h-4.5v14h6.667c2.945 0 5.333-2.388 5.333-5.334v-21.332c0-2.946-2.387-5.334-5.333-5.334z"></path>
</svg>

以下是我修改後的語法:

<svg>
	<symbol id="icon-facebook" viewBox="0 0 32 32">
		<path d="M26.667 0h-21.334c-2.945 0-5.333 2.388-5.333 5.334v21.332c0 2.946 2.387 5.334 5.333 5.334h10.667v-14h-4v-4h4v-3c0-2.761 2.239-5 5-5h5v4h-5c-0.552 0-1 0.448-1 1v3h5.5l-1 4h-4.5v14h6.667c2.945 0 5.333-2.388 5.333-5.334v-21.332c0-2.946-2.387-5.334-5.333-5.334z"></path>
	</symbol>
</svg>

你可以發現我改用<symbol>管理 icon,並且移除了他們固定的樣式,當然要更加結構化,可以再加上<title><def>等標籤。

如果已經接觸過 SVG 的朋友,應該會比較清楚這兩者寫法的差異,但重點在於,如果不把 IcoMoon 寫的樣式移除,我們將無法用 CSS 控制這些樣式,所以請特別記住這一點!

我也有把 IcoMoon 跟我修改後的 SVG 檔案放到網站上,大家可以自行打開原始碼比對。


以上跟大家介紹 SVG sprite 的使用方法,以及該怎麼管理。希望對大家有所幫助囉 😊


  • Wensen Leung

    最近做了一个项目,把现有的iconfont项目转换成SVG,已经支持绝大部分github上流行的项目。希望可以帮助大家升级到SVG sprite的方案: http://leungwensen.github.io/svg-icon/