使用SVG作為icon的方法

使用SVG作為icon的方法

就開開心心的用fontawesome不好嗎? 早點下班不好嗎?

icon的使用方法

相信大家在專案中若是需要使用icon的話,第一個一定會想到fontawesome, 他們的icon量非常多且齊全,且又能免費使用。

若是使用fontawesome時,icon會像這樣使用

<i class="fas fa-search"></i>

然後我們就能得到一個可愛的放大鏡小icon。


fontawesome - search icon

通常這種icon庫的做法是使用iconfont,font意思是字體,也就是說將icon作為一種字體來使用,因此,這些iconfont也能在css中被當作字體對待,可以套用像是color或fontsize等樣式變化。

但是其實在使用icon時,還有另外一種選擇 - SVG

什麼是SVG?

SVG, Scalable Vector Graphics, 可縮放矢量圖形,這種圖形其實是一行行的程式碼組成的,原理就是記錄這個圖形中一筆一畫(稱為path)的起始點和終點之間的向量,也因此這種圖片可以任意縮放而不會失真。

一個放大鏡的svg圖片會長這樣

1
2
3
4
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black" width="18px" height="18px">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
</svg>


Material Design - search icon

沒錯,你可以直接把這段程式碼丟到html中,圖片就會出現了。

由於不屬於iconfont, 所以你不能像對待文字那樣操控它的樣式,svg的樣式必須透過特殊的語法操作, 像是stroke, fill等等(詳情可以參考MDN), 若是你把svg想像成用程式碼在畫布上繪圖, 可能會比較容易記憶這些語法。

使用SVG icon有什麼好處?

事實上,網路上已經有許多討論svg優於iconfont的文章了,我想我一定不能寫得比他們更好,因此我在這裡直接貼出來,若是你有興趣的話可以參考看看,或是google。

Inline SVG vs Icon Fonts - CSS Tricks

为什么要用SVG?svg与iconfont、图片多维度对比

… and more!

總結我讀過的文章以及個人體驗,svg icon有以下幾個優點:

  1. 利於SEO
    svg可以像圖片一樣設定alt屬性, 因此搜尋引擎可以解讀它

  2. 不易破圖
    svg就是一段段程式碼,只要放到code裡就會出現, 但是fontawesome有時卻會因為不明原因無法顯示(我太菜,真的不懂為什麼)

  3. 較輕量
    使用iconfont會需要載入eot、ttf、woff等字體檔,但svg的話就不用

  4. 可客製化
    事實上這是我開始研究SVG的主因, 由於公司的UI想要自己設計所有icon,主管又不想再使用第三方icon庫,最後想出來的解法就是使用svg來取代原有的iconfont

如何使用?

使用symbol & use

MDN - symbol

具體請見下例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
在頁面某處放置所有需要的svg
<svg>
注意: 使用的是symbol標籤, 且有id作為辨識
<symbol id="search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black" width="18px" height="18px">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
</symbol>
</svg>

在需要的地方使用use
<svg class="icon search">
href指向需要的icon的id作為定位
<use href="#search-icon"></use>
</svg>

這種方式有個缺點,use標籤其實並不是正式的dom element, 而是shadow dom, 這種元素並不能被css操作, 因此不太方便

使用js做替換

這是目前我們公司使用的做法, 先將所有icon的svg都放置在一個叫iconmap.html的檔案中,先以ajax的方式呼叫後載入

1
2
3
4
5
6
7
<div class="iconmap" style="display: none">
存放的svg
<svg id="icon-search" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black" width="18px" height="18px">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
</svg>
</div>

在iconmap載入後會使用js先爬梳過整個map, 將所有svg標籤以id為名稱, 值為svg的html內容,存成一個叫icon store的物件

類似這樣

1
2
3
4
5
6
let iconstore = {
search: `<svg id="icon-search" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black" width="18px" height="18px">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
</svg>`;
}

使用的方式是, 以特殊的標籤或class作為辨識, 告訴js這是一個需要被替換icon的元素, 接著在頁面載入時會爬梳整個dom, 找到需要被替換的icon動態進行替換

html結構

1
2
3
4
<p>
使用一個客製化的tag作為辨識, class則是需要的icon名稱
我需要一個icon! <icon class="icon-search"></icon>
</p>

公司是使用jquery

1
2
3
4
$('icon').each(function(icon) {
// 一些字串處理來取得正確的名稱....
$(this).html(`${iconStore[$(this).attr('class').split('-')[1]]}`);
})

這種方法是直接將svg替換元素, 因此可以用css操作

大概是這樣子做替換的,我們公司使用的是自己的一套框架,因此這邊寫的只是大致的實現思路,具體就不寫出來了
其實這個方法在實務上還有很多需要調整的地方,也踩過不少雷,到現在還不敢說很完善,像是有些時候需要icon動態切換, 或是iconmap載入的時機太晚而產生的icon缺失等等。若是你的網頁很靜態沒有很多js切換icon的動作的話,還是可以參考看看的

在php中使用

這是最近學習php後才會使用的辦法,參考的是這篇文章

原理跟第二種方法很像, 都是將icon統一存放在一個外部環境做動態替換, 不過這次是存成一個個php

icon-search.php

1
2
3
4
5
6
7
8
<?php 

echo '<svg class="icon-search" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
</svg>';

?>

html

1
2
3
4
5
6
7
    <div class="container">
<a href="search.php" class="navItemLink">
Search
// 這個php區塊會被替換
<?php include("includes/icon/search.php")?>
</a>
</div>

這種方法我覺得也滿不錯的, 易讀且不易失敗, 但僅限php使用, 且組字串有點不方便

1
2
3
4
5
echo "<p>這裡會有一個icon";

include("includes/icon/search.php");

echo "</p>";

基於echo中不能再包一個php區塊的緣故, 因此使用include, 導致字串會被切割開來, 若是html複雜的話就有點難寫

結果就會像這樣

總結

svg和iconfont其實都是很好的icon作法, 雖然現在很多呼籲svg取代iconfont的聲音, 但我認為iconfont親民好上手的特性真的很難取代。

若是你也在尋找使用svg替代iconfont的做法的話,希望這篇文章能成為一點參考,另外,搜尋inline svg也可以找到許多不錯的文章。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×