使用 SVG 符號和 CSS 變量實現多彩圖標
使用圖片和 CSS 精靈製作 web 圖標的日子一去不復返了。隨著 web 字體的爆發,圖標字體已經成為在你的 web 項目中顯示圖標的第一解決方案。
字體是矢量,所以你無須擔心分辨率的問題。他們和文本一樣因為擁有 CSS 屬性,那就意味著,你完全可以應用 size
、 color
和 style
。你可以添加轉換、特效和裝飾,比如旋轉、下劃線或者陰影。
怪不得類似 Font Awesome 這類項目僅僅在 npm 至今已經被下載了超過 1500 萬次。
可是圖標字體並不完美, 這就是為什麼越來越多的人使用行內 SVG 。CSS Tricks 寫了圖標字體劣於原生 SVG 元素的地方:銳利度、定位或者是因為跨域加載、特定瀏覽器錯誤和廣告屏蔽器等原因導致的失敗。現在你可以規避絕大多數這些問題了,總體上使用圖標字體是一個安全的選擇。
然而,還是有一件事情對於圖標字體來說是絕對不可能的:多色支持。只有 SVG 可以做到。
設置 SVG 標誌圖標
行內 SVG 的問題是,它會非常冗長。你肯定不想每次使用同一個圖標的時候,還需要複製/粘貼所有座標。這將會非常重複,很難閱讀,更難維護。
通過 SVG 符號圖標,你只需擁有一個 SVG 元素,然後在每個需要的地方引用就好了。
先添加行內 SVG ,隱藏它之後,再用
包裹它,用 id
對其進行識別。
整個 SVG 標記被一次性包裹並且在 HTML 中被隱藏。
然後,所有你要做的是用一個 標籤將圖標實例化。
這將會顯示一個初始 SVG 圖標的副本。
**就是這樣了!**看起來很棒,是吧?
你可能注意到了這個有趣的 xlink:href
屬性:這是你的實例與初始 SVG 之間的鏈接。
需要提到的是 xlink:href
是一個棄用的 SVG 屬性。儘管大多數瀏覽器仍然支持,你應該用 **href**
替代。現在的問題是,一些瀏覽器比如 Safari 不支持使用 href
進行 SVG 資源引用,因此你仍然需要提供 xlink:href
選項。
安全起見,兩個都用吧。
添加一些顏色
不像是字體, color
對於 SVG 圖標沒有任何作用:你必須使用 fill
屬性來定義一個顏色。這意味著他們將不會像圖標字體一樣繼承父文本顏色,但是你仍然可以在 CSS 中定義它們的樣式。
// HTML // CSS .icon { width: 100px; height: 100px; fill: red; }
在這裡,你可以使用不同的填充顏色創建同一個圖標的不同實例。
// HTML // CSS .icon { width: 100px; height: 100px; } .icon-red { fill: red; } .icon-blue { fill: blue; }
這樣就可以生效了,但是不完全符合我們的預期。目前為止,我們所有做的事情可以使用一個普通的圖標字體來實現。我們想要的是在圖標的位置可以有不同的顏色。我們想要向每個路徑上填充不同顏色,而不需要改變其他實例,我們想要能夠在必要的時候重寫它。
首先,你可能會受到依賴於特性的誘惑。
// HTML // CSS .icon-colors .path1 { fill: red; } .icon-colors .path2 { fill: green; } .icon-colors .path3 { fill: blue; }
不起作用。
我們嘗試設置 .path1
、 .path2
和 .path3
的樣式,彷彿他們被嵌套在 .icon-colors
裡,但是嚴格來說,並非如此。 標籤不是一個會被你的 SVG 定義替代的佔位符。這是一個引用將它所指向內容複製為 shadow DOM 。
**那接下來我們該怎麼辦?**當子項不在 DOM 中時,我們如何才能用一個區域性的方式影響子項?
CSS 變量拯救世界
在 CSS 中,一些屬性從父元素繼承給子元素。如果你將一個文本顏色分配給 body
,這一頁中所有文本將會繼承那個顏色直到被重寫。父元素沒有意識到子元素,但是可繼承的樣式仍然繼續傳播。
現在有個問題:我們想傳遞不同顏色給原始 SVG 的不同路徑,但是隻能從一個 fill
屬性裡繼承。
這就需要 CSS 變量了。
.parent { --custom-property: red; color: var(--custom-property); }
所有 .parent
的子項都有紅色文本。
.parent { --custom-property: red; } .child { color: var(--custom-property); }
所有嵌套在 .parent
標籤裡的 .child
都有紅色文本。
現在,讓我們把這個概念應用到 SVG 符號裡去。我們將在 SVG 定義的每個部分使用 fill
屬性,並且設置成不同的 CSS 變量。然後,我們將給它們分配不同的顏色。
// HTML // CSS .icon-colors { --color-1: #c13127; --color-2: #ef5b49; --color-3: #cacaea; }
然後… 生效了 !
現在開始,為了用不同的顏色方案創建實例,我們所需要做的是創建一個新類。
// HTML // CSS .icon-colors-alt { --color-1: brown; --color-2: yellow; --color-3: pink; }
.icon-monochrome { fill: grey; }
怎樣命名我的 CSS 變量?
當提到在 CSS 中命名,通常有兩條途徑:描述的或者語義的。描述的意思是告訴一個顏色是什麼:如果你存儲了 #ff0000
你可以叫它 --red
。語義的意思是告訴顏色它將會被如何應用:如果你使用 #ff0000
來給一個咖啡杯把手賦予顏色,你可以叫它 --cup-handle-color
。
描述的命名也許是你的本能。看起來更乾脆,因為#ff0000
除了咖啡杯把手還有更多地方可以被使用。一個
--red
CSS 變量可被複用於其他需要變成紅色的圖標路徑。畢竟,這是實用主義在 CSS 中的工作方式。並且是一個良好的系統。問題是,在我們的案例裡,我們不能把零散的類應用於我們想設置樣式的標籤。實用主義原則不能應用,因為我們對於每個圖標有單獨的引用,我們不得不通過類的變化來設置樣式。
使用語義類命名,例如 --cup-handle-color
,對於這個情況更有用。當你想改變圖標一部分的顏色時,你立即知道這是什麼以及需要重寫什麼。無論你分配什麼顏色,類命名將會一直關聯。
默認還是不要默認,這是個問題
將你的圖標的多色版本設置成默認狀態是很有誘惑力的選擇。這樣,你無需設置額外樣式,只需要在必要的時候可以添加你自己的類。
有兩個方法可以實現::root 和 var() default 。
:root
在 :root
選擇器中你可以定義所有你的 CSS 變量。這將會把它們統一放在一個位置,允許你『分享』相似的顏色。 :root
擁有最低的優先度,因此可以很容易地被重寫。
:root { --color-1: red; --color-2: green; --color-3: blue; --color-4: var(--color-1); } .icon-colors-alt { --color-1: brown; --color-2: yellow; --color-3: pink; --color-4: orange; }
然而,這個方法有一個主要缺點。首先,將顏色定義與各自的圖標分離可能會有些讓人疑惑。當你決定重寫他們,你必須在類與 :root
選擇器之間來回操作。但是更重要的是,它不允許你去關聯你的 CSS 變量,因此讓你不能複用同一個名字。
var() 默認
你可以用 var()
功能來把一個 CSS 變量分配給一個屬性,並且它的第二個參數可以設置為某個默認值。
設置默認值會很有用,但是這是一個折中方案。我建議你不要形成習慣,只在對給定項目有幫助的時候做這件事情。
How browser-friendly is all that?
CSS 變量與大多數現代瀏覽器兼容,但是就像你想的那樣, Internet Explorer 完全不兼容。因為微軟要支持 Edge 終止了 IE11 開發, IE 以後也沒有機會趕上時代了。
現在,僅僅是因為一個功能不被某個瀏覽器(而你必須適配)兼容,這不意味著你必須全盤放棄它。在這種情況下,考慮下優雅降級:給現代瀏覽器提供多彩圖標,給落後瀏覽器提供備份的填充顏色。
如果你使用 Sass 的話,這個可以被抽象為一個 @mixin
。
@mixin icon-colors($fallback: black) { fill: $fallback; @content; }
現在,你可以任意定義顏色方案而無需考慮瀏覽器兼容問題了。
.cup { @include icon-colors() { --cup-color: red; --smoke-color: grey; }; } .cup-alt { @include icon-colors(green) { --cup-color: green; --smoke-color: grey; }; }
在不同的瀏覽器中查看這個 pen 。在最新版本的 Firefox , Chrome 和 Safari 中,最後兩隻杯子各自擁有紅色杯身灰色煙氣和藍色杯身灰色煙氣。在 IE 和 版本號小於 15 的 Edge 中,第三個杯子的杯身與煙氣全部都是紅色,第四個則全部是藍色! ✨
閱讀更多 前端圈 的文章