小圖標(biāo),大學(xué)問(wèn)

圖標(biāo)雖小,里面的門(mén)道可一點(diǎn)都不少。甚至可以說(shuō),圖標(biāo)的演化是 Web 技術(shù)演化的一個(gè)縮影。本文將帶你回顧一下圖標(biāo)簡(jiǎn)史,了解一下圖標(biāo)技術(shù)的來(lái)龍去脈。

古代:一個(gè)圖標(biāo)一張圖

史前時(shí)代的圖標(biāo),正如我們的直覺(jué)一樣,就是一張圖片。那時(shí)候的網(wǎng)絡(luò)很慢,一分鐘只夠下載一個(gè)頁(yè)面,因此內(nèi)容為王,美觀是次要的,“沒(méi)什么用”的圖標(biāo)還沒(méi)有被人們視為頁(yè)面上的必備元素。圖標(biāo)個(gè)數(shù)少、使用頻率低,自然就沒(méi)人在上面花心思了。

近代:CSS Sprites(雪碧圖)

隨著網(wǎng)上內(nèi)容迅速豐富,內(nèi)容的比拼已經(jīng)沒(méi)有更多花樣可玩了,于是網(wǎng)站的競(jìng)爭(zhēng)轉(zhuǎn)向了“用戶體驗(yàn)”領(lǐng)域。當(dāng)然,后來(lái)內(nèi)容又重新回到了舞臺(tái)中央,不過(guò)這已經(jīng)是后話了。

在體驗(yàn)方面追求差異化的方式很多,而在寬帶網(wǎng)絡(luò)還不夠普及的時(shí)代,最直觀的方面就是加載速度。 然而“一個(gè)圖標(biāo)一張圖”的方式在加載速度方面受到了嚴(yán)重限制。限制主要來(lái)自兩個(gè)方面:建立連接的時(shí)間,和瀏覽器的并發(fā)下載數(shù)量限制。前者來(lái)自 HTTP 協(xié)議,而后者則來(lái)自瀏覽器的實(shí)現(xiàn)。

CSS Sprites

HTTP 方面的限制很容易理解,代理、DNS、握手、發(fā)送請(qǐng)求、TTFB 的時(shí)間對(duì)于像圖標(biāo)這樣的小文件來(lái)說(shuō)很可能遠(yuǎn)超下載內(nèi)容的時(shí)間。

瀏覽器的并發(fā)限制其實(shí)在技術(shù)上來(lái)說(shuō)是很有必要的。如果不限制并發(fā)下載數(shù),一方面瀏覽器就會(huì)開(kāi)很多個(gè)線程,用戶的機(jī)器受不了;另一方面服務(wù)器也會(huì)收到大量的并發(fā)請(qǐng)求,服務(wù)器也受不了,很容易壓垮一些技術(shù)不過(guò)硬的網(wǎng)站。

瀏覽器限制并發(fā)下載數(shù),就會(huì)導(dǎo)致超出并發(fā)限制的請(qǐng)求被迫進(jìn)行排隊(duì),對(duì)于圖標(biāo)、圖片、css、js 等小文件很多的頁(yè)面來(lái)說(shuō),即使網(wǎng)速已經(jīng)較快,這種排隊(duì)也可能會(huì)持續(xù)很久。

顯然,優(yōu)化的方向就是減少并發(fā)下載的需求。因此,優(yōu)化的方案也就顯而易見(jiàn)了:把各種小圖標(biāo)拼合成一個(gè)大圖,然后想辦法讓瀏覽器把它重新切成多個(gè)就可以了。恰好,瀏覽器有一個(gè)特性叫background-position,也就是說(shuō)假如我們把這張大圖設(shè)置為當(dāng)前元素(寬w、高h)的背景,并且指定了 background-position(x, y),那么當(dāng)前元素的背景就是從大圖上 (x, y, x+w, y+h) 截取出來(lái)的那個(gè)區(qū)域。這樣一來(lái),就把 N 個(gè)并發(fā)下載合并成了一張大圖和一個(gè) css 文件。

這種方案如果做手動(dòng)拼合是非常繁瑣的,因此有人開(kāi)發(fā)了工具來(lái)整合到前端工具鏈中,并且 UX 的一些工具也逐漸提供了直接導(dǎo)出雪碧圖的功能。

近代:Data URL

除了拼合成雪碧圖之外,還有另一種技術(shù)也可以減少并發(fā)下載請(qǐng)求,那就是 Data URL。

簡(jiǎn)單來(lái)說(shuō),Data URL 就是這樣的形式:
data:[<mediatype>][;base64],<data>,
比如 data:image/svg+xml;utf8,<svg ... > ... </svg>
也可以對(duì) data部分進(jìn)行 base64 編碼,
比如data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDo

Data URL

對(duì)于瀏覽器來(lái)說(shuō),Data URL 和普通的 http URL 沒(méi)有什么區(qū)別 —— 除了不用額外下載。因此,凡是能用 http URL 的地方都可以換成 Data URL,比如 html 中的 <img src="...">,css 中的 background-image: url(...)。這樣一來(lái),就把圖標(biāo)的下載合并到了 html/css 的下載過(guò)程中。

但是,這種方式也有缺點(diǎn),那就是拖慢了整體渲染速度。

通常來(lái)講,瀏覽器的下載優(yōu)先級(jí)是 html > css > 圖片等資源的,因此我們經(jīng)常看到一個(gè)網(wǎng)站展現(xiàn)出來(lái)之后,里面的圖片還只顯示了一半,過(guò)一會(huì)兒才會(huì)完全顯示。如果我們把大量圖標(biāo)塞到 css 甚至 html 中,就會(huì)增大它們的體積,導(dǎo)致首屏展現(xiàn)變慢。

所以,是否使用 Data URL 技術(shù)需要仔細(xì)權(quán)衡,根據(jù)性能測(cè)量數(shù)據(jù)進(jìn)行優(yōu)化。

現(xiàn)代:字體圖標(biāo)

隨著視網(wǎng)膜屏幕的登場(chǎng),圖標(biāo)面臨著新的嚴(yán)峻挑戰(zhàn),那就是分辨率。

通常來(lái)說(shuō),圖標(biāo)文件的分辨率和屏幕的邏輯分辨率是一樣的,但是在視網(wǎng)膜屏下,這個(gè)論斷不再成立。如果視網(wǎng)膜屏的設(shè)備像素比(devicePixelRatio,簡(jiǎn)稱 dpr)是 3,那么圖標(biāo)就需要三個(gè)像素才能在視網(wǎng)膜屏下繪制出一個(gè)完美的邏輯像素,否則就會(huì)有粗糙感。

即使不考慮下載大小的問(wèn)題,也需要對(duì)原有工具鏈進(jìn)行改造才行。那不如干脆試試另一種方案。

近代的另一項(xiàng)發(fā)明派上了用場(chǎng),那就是“自定義字體”。

這本來(lái)是為了解決讓瀏覽器顯示更好看的文字而創(chuàng)造的技術(shù),比如要想用一種用戶機(jī)器上沒(méi)有的字體顯示藝術(shù)字,我們只需要提供一個(gè)字體文件,這些字體文件包含我們要用的那些文字的字體輪廓數(shù)據(jù)就可以了。這些輪廓數(shù)據(jù)是矢量數(shù)據(jù),用來(lái)表示每個(gè)字的“畫(huà)法”:從 0,0 開(kāi)始,以 50%,10% 為控制點(diǎn),畫(huà)一條貝塞爾曲線到 100%,30%。顯然,這種數(shù)據(jù)是不會(huì)受到屏幕分辨率影響的,就像我們?nèi)粘?吹降奈淖忠粯?,無(wú)論把它放到多大,它都是平滑而且不失真的。事實(shí)上,這正是一切矢量繪圖技術(shù)共同的優(yōu)點(diǎn)。請(qǐng)記住它,因?yàn)楹竺嫖覀冞€會(huì)用到另一種矢量繪圖技術(shù)。

既然我們可以通過(guò)控制顯示數(shù)據(jù),把字母 A 顯示為手寫(xiě)體的 A,那么我們是不是也可以把它顯示成一個(gè)看起來(lái)和 A 完全不一樣的圖標(biāo)呢?比如……一座房子?當(dāng)然可以,事實(shí)上,這正是字體圖標(biāo)的基本原理。

除了支持平滑縮放的優(yōu)勢(shì)以外,字體圖標(biāo)還有另一個(gè)優(yōu)勢(shì),那就是它本身就是文字。它會(huì)受到字號(hào)、前景色、行高等參數(shù)的控制,和普通文字沒(méi)有任何區(qū)別。而圖標(biāo),在實(shí)際應(yīng)用中經(jīng)常會(huì)和普通文字一起混排,這些特點(diǎn)正是我們想要的。

字體圖標(biāo)

不過(guò),字體圖標(biāo)也有一些缺點(diǎn)。

首要的缺點(diǎn)是單色。由于字體中只有矢量數(shù)據(jù),沒(méi)有顏色數(shù)據(jù),因此,字體圖標(biāo)必然是單色的。這在一些場(chǎng)景下是不夠用的。

其次是工具鏈復(fù)雜。雖然有一些工具可以幫你把一組 svg 文件拼合成一個(gè)字體文件,但是它們對(duì) svg 的格式有嚴(yán)格的要求,不是任何一個(gè) svg 都可以用的。你很難向 UX 解釋什么樣的圖能用、什么樣的圖不能用。其次,即使是可用的 svg,你也很難告訴工具每個(gè)圖標(biāo)的字體基線是哪個(gè)(通俗來(lái)說(shuō),基線就是你這個(gè)圖標(biāo)的底部和字母 g 的底部對(duì)齊,還是和字母 h 的底部對(duì)齊)。

基于這些特點(diǎn),在普通的團(tuán)隊(duì)中使用自定義字體圖標(biāo)是相當(dāng)困難的。不過(guò)好在還有不普通的團(tuán)隊(duì),比如 FontAwesome,他們專門(mén)制作、維護(hù)了一組免費(fèi)圖標(biāo)貢獻(xiàn)給開(kāi)源社區(qū)。如果你需要的圖標(biāo)恰好是其中之一,那么直接用就可以了,你需要做的只是引入它的 css 之后,在 html 中使用<i class="fa fa-home"></i>。

當(dāng)代:svg 圖標(biāo)

FontAwesome 雖好,但也不是萬(wàn)能的。它往往不足以融入 UX 的 Design System,而 UX 顯然也不愿意削足適履,為了圖標(biāo)而改變自己的整體設(shè)計(jì)。

因此,對(duì)開(kāi)發(fā)團(tuán)隊(duì)更友好的方式仍然應(yīng)該是高度可定制的、方便單個(gè)處理的。如果能直接使用 UX 提供給我們的 svg 文件顯然是最理想的。問(wèn)題在于,該怎么用。這里面的門(mén)道可就多了。

圖標(biāo)

在這種場(chǎng)景下雪碧圖和 Data URL 仍然是可用的,因?yàn)樗鼈冎恍枰獔D片,而不管圖片的格式,svg 也是圖片,也有同樣的優(yōu)缺點(diǎn) —— 但能支持視網(wǎng)膜屏。

不過(guò),svg 的特點(diǎn),讓我們還有了一些另外的用法。

首先,可以把 svg 內(nèi)聯(lián)到 html 中。svg 和 html 在語(yǔ)法上非常像,都是 xml 語(yǔ)系,只是使用了不同的命名空間(xmlns),因此我們可以把 svg 作為一個(gè)元素內(nèi)聯(lián)到 html 中,現(xiàn)代瀏覽器可以正確地解釋它們。這種用法比較自然,html 中引入的 css 也同樣可以作用于 svg 內(nèi)部的元素上,圖文可以無(wú)縫整合在一起。

不過(guò)這種用法有兩個(gè)問(wèn)題。其一是 svg 中各個(gè)元素的 id 會(huì)并入頁(yè)面的命名空間中,比如在 svg 中引用了一個(gè)名為 a 的過(guò)濾器,那么如果 html 或另一個(gè) svg 中也定義了它,就會(huì)互相沖突。在稍大點(diǎn)的項(xiàng)目中要解決這種沖突會(huì)相當(dāng)麻煩。其二是如果這個(gè)圖標(biāo)出現(xiàn)很多次,它的內(nèi)容就會(huì)在 html 中重復(fù)很多遍,體積也會(huì)相應(yīng)的增大。

好在,svg 有一種機(jī)制可以解決這個(gè)問(wèn)題,也就是use 標(biāo)簽。使用 use 標(biāo)簽,你可以根據(jù) id 引用本頁(yè)面中的 svg 元素,甚至來(lái)自其它 svg 文件中的元素。比如要引用本頁(yè)面中的 id 為 arect 元素,你只需要寫(xiě) <use xlink:href="#a">即可,并且在這里你可以指定自己的 svg 屬性,以覆蓋原始元素上的 svg 設(shè)置。這樣一來(lái),圖標(biāo)內(nèi)容被重復(fù)很多遍的問(wèn)題就解決了。如果寫(xiě)成 <use xlink:href="path/to/file.svg#a"> 則可以引用外部文件 path/to/file.svg 中定義的元素,那么 id 沖突的問(wèn)題也同樣解決了,因?yàn)樗鼈儾辉谕粋€(gè)命名空間。

不過(guò),這種方式相對(duì)于字體圖標(biāo)還有兩個(gè)缺點(diǎn):

一是圖標(biāo)的顏色不會(huì)自動(dòng)跟隨文字顏色。比如原始元素定義的 rect 是紅色的,那么無(wú)論你把它混排到什么顏色的文字中,它都是紅色的。難道我們要在每個(gè)使用它的地方都手動(dòng)覆蓋一下顏色嗎?當(dāng)然不必,我們還有另一個(gè)特性可以解決這個(gè)問(wèn)題,那就是 currentColor。這是一個(gè)預(yù)定義的特殊顏色值,它的意思就是取當(dāng)前的文字顏色。比如當(dāng)你寫(xiě)<rect fill="currentColor"></rect> 時(shí),把它混排到灰色文字中,這個(gè)rect的填充色就是灰色的,混排到藍(lán)色文字中就是藍(lán)色的。而且,這個(gè)圖標(biāo)的其它部分你仍然可以指定特定的顏色,比如圖標(biāo)主體部分跟隨文字顏色,而某個(gè)特殊區(qū)域總是顯示為藍(lán)色。 經(jīng)過(guò)這樣的處理之后,你不但可以彌補(bǔ)相對(duì)于字體圖標(biāo)的缺點(diǎn),還可以更進(jìn)一步,支持彩色圖標(biāo)了!即使你不需要彩色圖標(biāo),憑借 svg 對(duì)元素透明度的支持,也可以讓你的圖標(biāo)比字體圖標(biāo)更加豐富多彩。

二是圖標(biāo)的大小不會(huì)自動(dòng)跟隨字體大小。不過(guò)這個(gè)就好解決了,因?yàn)?css 中有一個(gè)特性就是把當(dāng)前字號(hào)作為尺寸單位,也就是 em,比如圖標(biāo)大小設(shè)置為1em就會(huì)讓圖標(biāo)的實(shí)際尺寸跟當(dāng)前字號(hào)一致。

當(dāng)代:合字(Ligature)

你知道“囍”字嗎?嚴(yán)格來(lái)說(shuō),它不是一個(gè)字,而是一個(gè)“合字”。也就是說(shuō)這是兩個(gè)漢字,只是顯示成了一個(gè)字的樣子。只是因?yàn)樗浅3R?jiàn),所以在字庫(kù)中給了它一個(gè)單獨(dú)的位置。但是大多數(shù)類似的文字是得不到這種特殊待遇的,比如“孔孟好學(xué)”的合體,以及“biangbiang面”中的“biang”字;字母上的聲調(diào)(比如漢語(yǔ)拼音)也是合字。

那么,要如何用標(biāo)準(zhǔn)的方式來(lái)顯示這些合字呢?實(shí)際上,現(xiàn)代的字體庫(kù)早就已經(jīng)支持合字了,只是在現(xiàn)實(shí)中用得不多,一般人沒(méi)怎么注意罷了。不過(guò),在圖標(biāo)領(lǐng)域,它重新找回了用武之地。我所知道的最早使用合字的圖標(biāo)體系是 Google 的 Material Design,比如用 <i class="material-icons">home</i>就可以顯示出一座房子,它是怎么工作的呢?實(shí)際上,material-icons 類為這個(gè) i元素指定了一個(gè)支持合字的字體庫(kù):'Material Icons',然后就會(huì)在字體庫(kù)中檢索出 home 這個(gè)合字對(duì)應(yīng)的單字,并且把那個(gè)單字顯示出來(lái)就可以了。換句話說(shuō),home 是某個(gè)單字的別名。

但是,我們?yōu)槭裁床幌?FontAwesome 那樣直接引用這個(gè)單字,而要用合字中轉(zhuǎn)一次呢?在回答這個(gè)問(wèn)題之前,我們先要知道一個(gè)概念,那就是:

訪問(wèn)互聯(lián)網(wǎng)并不是我們這些健全人的專利!

世界上有很多殘疾人,特別是視障人士,比如盲人、弱視等,甚至等我們老了都有可能加入他們的行列。他們?cè)L問(wèn)互聯(lián)網(wǎng)時(shí)難以像我們一樣憑視覺(jué)閱讀網(wǎng)頁(yè),而需要借助一種屏幕閱讀器。

屏幕閱讀器無(wú)法理解某個(gè)單字表示的是房子形狀的圖標(biāo),因此頁(yè)面的編寫(xiě)者就需要給這個(gè)圖標(biāo)加上特殊的 aria-label等屬性,以便屏幕閱讀器朗讀它們。這稱為 Accessibility(無(wú)障礙),簡(jiǎn)稱 a11y?;叵胍幌?,你加過(guò)幾個(gè)這種屬性?很多人都不加,因?yàn)槁闊?。但使用合字就不需要考慮這種問(wèn)題了,因?yàn)楹献直旧砭褪强勺x的,在 html 中的寫(xiě)法就像普通文本一樣。所以,你只要自然而然的使用合字,就已經(jīng)滿足了 a11y 的一些要求。

因此,雖然“合字”本身沒(méi)有多少新的技術(shù),但是我仍然把它歸于“當(dāng)代”,它值得作為一種趨勢(shì)受到重視。

圖標(biāo)在開(kāi)發(fā)中的其它方面

在實(shí)際的開(kāi)發(fā)工作中,還有一些問(wèn)題需要考慮。

圖標(biāo)開(kāi)發(fā)

第一個(gè)問(wèn)題是搖樹(shù)優(yōu)化,也就是說(shuō),我們沒(méi)有實(shí)際使用到的圖標(biāo)應(yīng)該自動(dòng)被優(yōu)化掉,而不應(yīng)該讓我們手工檢查哪些圖標(biāo)沒(méi)用到,并且從源碼中刪掉。前面的大多數(shù)方案都難以給出完美的答案,只有內(nèi)聯(lián) svg 方式是一個(gè)相對(duì)理想的方案。簡(jiǎn)單來(lái)說(shuō),寫(xiě)一個(gè)構(gòu)建工具,當(dāng)你在 html 中發(fā)現(xiàn)了一個(gè) <img src="path/to/file.svg"> 時(shí),把這個(gè) svg 文件的內(nèi)容讀出來(lái),并且內(nèi)聯(lián)到 html 中。這樣,只要一個(gè)文件從未被引用過(guò),就會(huì)自動(dòng)優(yōu)化掉。如果你用基于 WebPack 的構(gòu)建工具,可以引入我寫(xiě)的一個(gè) “markup-inline-loader”。當(dāng)然,如果你使用 Angular 這樣的現(xiàn)代框架,你就不需要為此做什么額外的工作了。你只要把每個(gè)圖標(biāo)做成一個(gè)組件,使用 svg 內(nèi)容作為模板,然后像普通組件一樣引用它就可以了。Angular 會(huì)自動(dòng)幫你優(yōu)化掉沒(méi)有引用過(guò)的組件。

第二個(gè)問(wèn)題是 SPA?,F(xiàn)代的前端應(yīng)用基本上都是單頁(yè)面應(yīng)用(SPA),因此往往并不需要同時(shí)下載大量的圖標(biāo),而是按需加載。因此,“古代”那種“一個(gè)圖標(biāo)一張圖”的方式未必就真的不可接受,針對(duì)你的實(shí)際業(yè)務(wù)場(chǎng)景,做一下鏈路分析,它沒(méi)準(zhǔn)反倒是最合適的方案。

第三個(gè)問(wèn)題是 svg 文件本身的優(yōu)化。很多工具導(dǎo)出的 svg 文件很啰嗦,里面有很多對(duì)于顯示沒(méi)有意義的東西。一些 svg 圖標(biāo)即使減小到原來(lái)體積的一半兒都不會(huì)影響顯示,因此,針對(duì) svg 本身做一些優(yōu)化也是有價(jià)值的。當(dāng)然,這事不必手工來(lái)做,有一個(gè)現(xiàn)成的工具可以做這事,它叫做 svgo,你只要運(yùn)行 npm i -g svgo 命令就可以全局安裝它了。你可以用 svgo命令對(duì)單個(gè)文件或者整個(gè)目錄做優(yōu)化;可以手工使用,也可以把它集成到工具鏈里。

結(jié)語(yǔ)

這些圖標(biāo)技術(shù),雖然出現(xiàn)時(shí)間上有先后,但并不是簡(jiǎn)單的替代關(guān)系,而是各有優(yōu)缺點(diǎn),適用于不同的場(chǎng)景。

隨著需求和技術(shù)條件的變化,選型策略也要做出調(diào)整,有些時(shí)候還要混合使用,以發(fā)揮各自的優(yōu)勢(shì)。

文/ThoughtWorks汪志成

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容