談談Web圖標

前段時間意圖模仿百度開放雲的UE風格時,想把標誌不同狀態的圓點摳出來作爲<img>放到自己項目中,結果一查發現他們不是<img>,而是@font-face方式實現,頓感自己的無知,因此有了這篇小結式的文章。

0x00 序

隨着前端技術的不斷革新,當前Web中的圖標(Icons)已經不再僅僅是侷限於<img>。除此之外,還有Sprites(俗稱雪碧圖)、Icon Font(字體圖標)、SVG Icon等等。本文就將探討一下這些方法在Web中實現圖標的利弊。

0x01 <img>標籤

<img>標籤是用來給Web頁面添加圖片的。而圖標(Icons)也算是圖片的一種,因此也可以在頁面中直接用<img>來加載圖標,且可以加載任何適用於Web頁面的圖標格式,例如:.jpg(或.jpeg)、.png、.gif。對於今天的Web,除了這幾種圖片格式之外,還可以直接引用.webp和.svg圖像(圖標)。

對於這種實現方式,其優點乏善可陳(最大的優點就是看起來很好下手),劣勢也是明顯的。

優勢

  • 更換簡單方便,只需要修改圖標路徑或覆蓋圖標文件名
  • 圖標大小易於掌握

劣勢

  • 增加HTTP請求數,如果頁面使用很多圖標,則會直接拉高HTTP請求書,從而影響了頁面加載性能。
  • 不利於適配各種終端和分辨率,可能會導致在Retina屏中圖標顯示模糊(除非加載的是矢量圖標.svg,或者一開始就加載了適合高PPI的圖標)
  • 不易修改圖標的樣式,比如顏色,陰影等
  • 不易維護

0x02 CSS Sprites(雪碧圖)

由於<img>的侷限性與不足,2004年3月Dave Shea提出了CSS Sprites的技術(在國內常常稱爲CSS雪碧,或者CSS精靈)。

現在有很多頁面都有采用這種技術,把頁面上的ICON、欄目背景、圖片按鈕等有規則的合併一張背景圖,然後利用background-position的負值來進行調節,實現背景圖片的定位。

早期CSS Sprites使用的都是位圖,且一般採用的都是.png文件格式。但是在現在的web環境中,若只使用位圖,會受到很多的限制,比如在Retina屏下,位圖會模糊。也就是說,爲了適配各種終端設備分辨,CSS Sprites不在侷限於位圖,也可以將SVG這樣的矢量圖集合在一起。其和位圖最大的不同之處可以根據設備分辨率,調整Sprites的尺寸,從而不影響圖標在設備的呈現質量。

優勢

  • 減少HTTP請求數
  • 可以是任意圖形,也可以是任意色彩
  • 兼容性極好(對於位圖的Sprites兼容性都非常的好,但對於SVG的Sprites,還是受到瀏覽器的限制,最起碼要支持SVG的瀏覽器才能得到支持)

劣勢

  • 增加開發時間,需要人肉或通過相關工具,將零散的圖形合併到一起,而不同的合併方式,圖形的色彩對Web的性能有直接的影響
  • 增加維護成本,要增加新的圖標合成進來,是件較難的事情,甚至直接會影響到前面又定位好的圖片。目前爲止,使用自動編譯工具,相對比人肉處理要理想一些
  • 圖片尺寸固定,在位圖的Sprites中無法通過CSS來修改圖標的大小,但在SVG的Sprites中可以配合CSS的background-size調整圖標的大小

0x03 字體圖標(Icon Font)

儘管CSS Sprites有其足夠的優勢,而且很多開發者都在使用這種技術,但是現如今,隨着Retina屏的出現,開發者不得不考慮面對各種高清屏幕的顯示效果,不得不對CSS Sprites說再見(主要是對位圖說再見)。

在這裏,我想扯開去說下響應式圖片。

響應式圖片(Responsive Image)

什麼是響應式圖片

響應式圖片是指:用戶代理根據輸出設備的分辨率不同加載不同類型的圖片,不會造成帶寬的浪費。同時,在改變輸出設備類型或分辨率時,能及時加載對應類型的圖片。

表面上看,這是一件非常簡單的事情,只要把圖片元素的高、寬屬性值都移去,然後設置max-width屬性爲100%即可。不過這麼做的前提是你必須要創建一幅儘可能高分辨率的圖片。

在當前響應式設計和自適應設計的流行下,很多web 應用往往都兼容手機、平板和PC,其中一個讓人比較頭痛的問題就是圖片的加載了。不同平臺顯然不可能用同一張大的圖片,這樣子不但浪費手機流量、影響網站載入速度並且在小屏幕下會很不清晰。讓瀏覽器根據分辨率自動識別圖片是最好的方法。

響應式解決方法

目前可能存在的兩種根據屏幕不同而輸出不同分辨率的圖片的方式:

  1. 可以使用<img>srcset屬性調用不同的圖像。srcset屬性包含一系列的逗號分隔值,一方面是指定圖像的url,另一方面指圖像將要顯示的條件。圖像條件我們可以看到有像素密度、視窗尺寸大小或者同時兩者都存在。可以說這個屬性的基本特徵就是:根據指定的條件來做選擇,使其工作。

    1)若圖片是固定大小(pixels),但是想要適應不同分辨率的屏幕,基本語法如下:

    <img alt="description"
        width="320" height="213"
        src="photo.jpg"
        srcset="photo-2x.jpg 2x, photo-3x.jpg 3x">
    • src="photo.jpg" 對於不支持srcset屬性的瀏覽器以及設備像素比爲1x的,將顯示這張圖片
    • srcset屬性中的每項都是<url> <density>x的形式, 例如photo-2x.jpg 2x;不同項的順序無所謂
    • 如果沒有設定width或者height,那麼瀏覽器會按照圖片原本的寬和高來除以設備像素比,例如3x資源被選中時,將渲染50%該圖片的寬和高。

    2)在一些情況下,srcset還可以根據屏幕的視窗尺寸的寬度來選擇圖片,這就相當於媒體查詢查到一個斷點時加載背景圖像。通過srcset,瀏覽器可以知道可用的圖片資源和寬度,通過sizes,可以知道對於給定的窗口寬度,應當展現什麼寬度的<img>,從而可以加載最佳資源。也就是說,我們無需指定設備像素比,瀏覽器能自己搞定。

    <img alt="description" 
        src="photo-689.jpg" 
        srcset="photo-689.jpg 689w,             
            photo-1378.jpg 1378w,             
            photo-500.jpg 500w,             
            photo-1000.jpg 1000w" 
        sizes="(min-width: 1066px) 689px,            
            (min-width: 800px) calc(75vw - 137px),            
            (min-width: 530px) calc(100vw - 96px),            
            100vw">

    若窗口寬度大於1066px,那麼<img>將會是689px。在一個1x的設備上,瀏覽器將下載photo-689.jpg,而在一個2x設備上,則會下載photo-1378.jpg圖片。

    • srcset中每一項都是<url> <width-descriptor>w的形式,例如photo-689.jpg 689w
    • sizes中每一項都是<media-condition> <image-element-width>的形式,最後一項是<image-element-width>
  2. 創建新元素或屬性:web開發者提議創建一個新的picture元素(類似HMTL5中的video這樣的元素),該元素中包含其他的圖片源,可以根據不同的屏幕調用不同的圖像。示例代碼如下:

    <picture alt="description">
        <source src="small.jpg">
        <source src="medium.jpg" media="(min-width: 400px)">
        <source src="large.jpg" media="(min-width: 800px)">
    </picture>

    其中的small.jpg是默認情況下顯示的圖片源,在後面兩個source元素則是在特定媒體查詢(media queries)條件下顯示的圖片——這也是開發者所喜歡的一種解決方案。

字體圖標

對於當前一些根據屏幕的不同來輸出不同分辨率的圖片的手段:

  • 通過image-set實現Retina屏幕下的圖像顯示
  • 通過媒體查詢,在不同的屏幕下調用不同的圖像;當你爲高分辨率屏幕適配網頁的時候(比如蘋果的Retina屏幕),一般會添加更大尺寸的圖片資源,並且使用CSS中的Media Query來識別並適配尺寸。但是如此一來,文件數量和大小會急劇增加,並且會增加代碼中的CSS選擇器的數量,引用更多的文件。
  • 使用img的srcset屬性調用不同的圖像
  • 使用標籤元素根據不同的屏幕調用不同的圖像

不管使用哪種方法,都不是一件易事,比如使用image-set和媒體查詢,只適合背景圖像;而對於srcset和picture方法僅適合Web引入圖像的情景。而且這些方法直到目前爲止在瀏覽器上都受到了很多的限制。

爲了解決屏幕分辨率對圖標影響的問題,字體圖標(Icon Font)就順勢而生了。字體圖標是一種全新的設計方式,更爲重要的是相比位圖而言,使用字體圖標可以不受限於屏幕分辨率,衝着這一點就具有非常強的優勢,而且字體圖標還具有一個優勢是,只要適合字體相關的CSS屬性都適合字體圖標,比如說:

  • 自由使用font-size變化圖標的大小
  • 自由使用color修改圖標顏色
  • 可以添加一些視覺效果,比如:陰影、旋轉、透明度
  • 兼容IE6

基於這些原因,現在Web開發中,使用字體來製作圖標的應用也越來越多。

通常應用步驟:

  1. font-face聲明字體

    @font-face {font-family: 'iconfont';
    src: url('iconfont.eot'); /* IE9*/
    src: url('iconfont.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
    url('iconfont.woff') format('woff'), /* chrome、firefox */
    url('iconfont.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
    url('iconfont.svg#iconfont') format('svg'); /* iOS 4.1- */
    }
  2. 定義使用iconfont的樣式

    .iconfont{font-family:"iconfont";
    font-size:16px;font-style:normal;}
  3. 挑選相應圖標並獲取字體編碼,應用於頁面

    <i class="iconfont">&#33</i>

優勢

  • 減少了HTTP的請求
  • 很容易地縮放、改變顏色、產生陰影、擁有透明效果
  • 瀏覽器兼容性較好
  • 可以到很CSS很好支持
  • 可以快速轉化形態
  • 可以做出跟位圖一樣可以做的事情
  • 本身體積更小

劣勢

  • 它們只能被渲染成單色或CSS3的漸變色
  • 使用限制性很大,除非你想花時間去創作你自己的字體圖標
  • 創作字體圖標很耗時間
  • 可訪問性差
  • 字體文件體積過大,直接影響頁面加載性能,特別是加載一個包含數百圖標的Fonts,卻只使用其中幾個圖標
  • 在不同的設備瀏覽器字體的渲染會略有差別,在不同的瀏覽器或系統中對文字的渲染不同,其顯示的位置和大小可能會受到font-size、line-height、word-spacing等CSS屬性的影響,而且這種影響調整起來較爲困難
  • 爲了實現最大程度的瀏覽器支持,可能要提供至少四種不同類型的字體文件。包括.ttf、.woff、.eot和.svg格式字體
  • 不兼容舊的手機瀏覽器:Opera mini,Android 2.1,Windows Phone 7.5-7.8
  • 在手機上可能與系統字體衝突

Icon Font的主要缺陷有以下幾條:

  1. 瀏覽器將其視爲文字進行抗鋸齒優化,有時得到的效果並沒有想象中那麼銳利。 尤其是在不同系統下對文字進行抗鋸齒的算法不同,可能會導致顯示效果不同
  2. Icon Font作爲一種字體,Icon顯示的大小和位置可能要受到font-sizeline-heightword-spacing等等CSS屬性的影響。icon所在容器的CSS樣式可能對icon的位置產生影響,調整起來很不方便。
  3. 使用上存在不便。首先,加載一個包含數百圖標的Icon Font,卻只使用其中幾個圖標,非常浪費加載時間。自己製作Icon Font以及把多個Icon Font中用到的圖標整合成一個Font也非常不方便
  4. 爲了實現最大程度的瀏覽器支持,可能要提供至少四種不同類型的字體文件。包括TTF、WOFF、EOT 以及一個使用 SVG 格式定義的字體。

0x04 SVG圖標

爲了適配各種分辨率,讓圖標顯示更完美,除了字體圖標之外,還可以使用SVG圖標。SVG圖標是一種矢量圖標。其實回過頭來看,字體圖標其實也是使用SVG封裝過的。

SVG(Scalable Vector Graphics)是一種古老的技術(不過字體應該更古老),字面理解爲可縮放矢量圖形,是一種基於xml的圖形,所以也可以理解爲是開發者通過代碼繪製的圖形,所以他不受分辨率影響(沒有馬賽克)。

爲什麼要使用SVG圖標

SVG圖標實際上是一個服務於瀏覽器的XML文件,而不是一個字體或像素的位圖。它是由瀏覽器直接渲染XML,在任何大小之下都會保持圖像清晰。而且文件中的XML還提供了很多機會,可以直接在代碼中使用動畫或者修改顏色,描邊等。不需要藉助任何圖形編輯軟件都可以輕鬆的自定義圖像。除此之外,SVG圖像也有過字體圖標的一個主要優勢:擁有多個彩色圖像的能力。

Inline SVG

所謂Inline SVG,就是直接把 SVG 寫入 HTML 中,這種方法簡單直接,而且具有最強的可調性。 使用這種方法,你可以使用 CSS 的fill屬性(文字顏色填充)/和stroke屬性(文字描邊)來控制填充顏色和邊線的顏色, 如果 SVG 圖標包含多個部分,你甚至可以設置每個部分的樣式。同時,使用 JavaScript 修改 SVG 和生成動畫效果都可以實現。
Inline SVG 作爲 HTML 文檔的一部分,不需要單獨請求。臨時需要修改某個圖標的形狀也比較方便。 但是 Inline SVG 使用上比較繁瑣,需要在頁面中插入一大塊 SVG 代碼因此不適合手寫,圖標複用起來也比較麻煩。
好在我們大部分的頁面都是由某種模板渲染出來的,無論是使用 PHP、Jinja2 還是 ERuby 模板語言, 都可可以定義一個函數來幫我們 include 這些 SVG。因此在很多情況下是很好的解決方案, 其不適合的主要使用場景就是純靜態頁面或者前後端分離客戶端頁面。

優勢

  • SVG圖標是矢量圖形文件,可以隨意修改大小,而且不會影響圖標質量
  • 可以使用CSS樣式來自定義圖標顏色,比如顏色、尺寸等效果
  • 所有SVG圖標可以全部放在一個SVG的文件中(SVG Sprites),節省HTTP的請求
  • 使用SMIL、CSS或者JavaScript可以製作動畫效果
  • 可以使用gzip的方式把文件壓縮到很小
  • 可以很精細的控制SVG圖標的每一部分

劣勢

  • 瀏覽器兼容性較差
  • 需要學習SVG相關知識
  • 需要了解使用製作軟件繪製SVG圖形或專業的SVG圖形編輯軟件

0x05 Data URIs

Data URIs是一種不太常見的方式。之前我們在CSS裏定義元素的背景圖片時,常使用像下面這種方式:

.icon {
  backgound-image: url(icons/a.png)
  /* ... */
}

而現在,url當中可以放置的可以不僅僅是指向資源的URL鏈接,而可以是數據本身。使用Data URIs,無論是圖片還是SVG,都可以將其編碼爲base64並直接寫入CSS。例如:

.icon{ 
  background: url(data:text/svg+xml;base64,<base64 encoded data>)
}

Data URI 的格式定義如下:

data:[<mime type>][;charset=<charset>][;base64],<encoded data>

使用這種方法,SVG Icon使用起來和Icon Font一樣只需要爲元素添加CSS即可,所有的資源都可以整合在一個CSS文件中,不需要額外引用SVG文件。這個任務只有簡單的字符串和編碼處理,基本不需要依賴非JavaScript的庫和資源。

但是Data URIs的劣勢也是明顯的,每次都需要解碼,這會阻塞CSS渲染;雖然可以通過分離出一個專用的CSS文件,但是這就需要增加一個請求,那和CSS Sprites、Icon Font和SVG相比就沒有任何優勢了。

0x06 純CSS實現

除了前面提到的幾種圖標實現方式以外,一些簡單的圖標也可以直接用純CSS實現。主要是通過設置元素爲position:relative;,設置:before:afterposition:absolute;,然後通過CSS相關的backgroundborderborder-radiustransform:rotate()、和z-index來交叉生成。

這裏推薦一個純CSS生成圖標的庫:http://saeedalipoor.github.io/icono/

優勢

  • 通常比其他圖標體積小很多

劣勢

  • 有瀏覽器兼容性問題
  • 能夠實現的圖標有限

0x07 如何選擇

前面介紹了Web中製作圖標的幾種常見方案,每種方案都有其自己的利弊。那在實際中要如何選擇呢?這需要根據自身所在的環境來做選擇:

  • 如果你需要信息更豐富的圖片,不僅僅是圖標時,可以考慮使用<img>
  • 使用的不是展示類圖形,而是裝飾性的圖形(包括圖標),而且這部分圖形一般不輕意改變,可以考慮使用PNG Sprites
  • 如果你的圖標之類需要更好的適配於高分辨率設備環境之下,可以考慮使用SVG Sprites
  • 如果僅僅是要使用Icon這些小圖標,並且對Icon做一些個性化樣式,可以考慮使用Icon Font
  • 如果你需要圖標更具擴展性,又不希望加載額外的圖標,可以考慮在頁面中直接使用SVG代碼繪製的矢量圖

當然,在實際開發中,可能一種方案無法達到你所需的需求,你也可以考慮多種方案結合在一起使用。

0x08 總結

全文主要介紹了Web中圖標的幾種方案之間的利與弊。相對而言,如果不需要考慮一些低版本用戶,就當前這個互聯網時代,面對衆多終端,較爲適合的方案還是使用SVG。不管是通過img直接調用.svg的文件還是使用SVG的Sprites,或者直接在頁面中使用SVG(直接代碼),都具有較大的優勢。不用擔心,使用的圖標在不同的終端(特別是在Retina屏)會模糊不清。而且SVG還有一個較大的優勢,你可以直接在源碼中對SVG做修改,特別是可以分別控制圖標的不同部分,加入動畫等。

當然,你或許會有衆多的顧慮,不懂SVG的怎麼破,就算不懂SVG,你也可以藉助SVG的圖形編輯軟件或者工具,來協助你。除此之外,除了這些方式在Web中嵌入圖標之外,對於一些簡單的小圖標,可以考慮直接使用CSS代碼來編寫,這種方式可能較爲費時費力,但其具有的優勢,我想大家都懂的。

感覺CSS3的潛力就像是鳴人體內的九尾狐妖一樣,能量巨大,不可限量。

0x09 參考資料

https://drafts.csswg.org/mediaqueries-3/

Inline SVG vs Icon Fonts,翻譯版在這裏

查看css屬性的瀏覽器兼容性:http://caniuse.com/#search=srcset

https://jakearchibald.com/2015/anatomy-of-responsive-images/

http://www.w3cplus.com/responsive/responsive-images-part-1-using-srcset.html

https://css-tricks.com/which-responsive-images-solution-should-you-use/

http://iconfont.cn/help/platform.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章