[譯] 使用 SVG 符號和 CSS 變量實現多彩圖標

使用 SVG 符號和 CSS 變量實現多彩圖標

使用圖片和 CSS 精靈製做 web 圖標的日子一去不復返了。隨着 web 字體的爆發,圖標字體已經成爲在你的 web 項目中顯示圖標的第一解決方案。css

字體是矢量,因此你無須擔憂分辨率的問題。他們和文本同樣由於擁有 CSS 屬性,那就意味着,你徹底能夠應用 sizecolorstyle 。你能夠添加轉換、特效和裝飾,好比旋轉、下劃線或者陰影。前端

怪不得相似 Font Awesome 這類項目僅僅在 npm 至今已經被下載了超過 1500 萬次android

但是圖標字體並不完美, 這就是爲何愈來愈多的人使用行內 SVG 。CSS Tricks 寫了圖標字體劣於原生 SVG 元素的地方:銳利度、定位或者是由於跨域加載、特定瀏覽器錯誤和廣告屏蔽器等緣由致使的失敗。如今你能夠規避絕大多數這些問題了,整體上使用圖標字體是一個安全的選擇。ios

然而,仍是有一件事情對於圖標字體來講是絕對不可能的:多色支持。只有 SVG 能夠作到。git

摘要 :這篇博文深刻闡述怎麼作和爲何。若是你想理解整個思惟過程,推薦閱讀。不然你能夠直接在 CodePen 看最終代碼。github

設置 SVG 標誌圖標

行內 SVG 的問題是,它會很是冗長。你確定不想每次使用同一個圖標的時候,還須要複製/粘貼全部座標。這將會很是重複,很難閱讀,更難維護。web

經過 SVG 符號圖標,你只需擁有一個 SVG 元素,而後在每一個須要的地方引用就行了。chrome

先添加行內 SVG ,隱藏它以後,再用 <symbol> 包裹它,用 id 對其進行識別。npm

<svg xmlns="http://www.w3.org/2000/svg" style="display: none">
  <symbol id="my-first-icon" viewBox="0 0 20 20">
    <title>my-first-icon</title>
    <path d="..." />
  </symbol>
</svg>
複製代碼

整個 SVG 標記被一次性包裹而且在 HTML 中被隱藏。後端

而後,全部你要作的是用一個 <use> 標籤將圖標實例化。

<svg>
  <use xlink:href="#my-first-icon" />
</svg>
複製代碼

這將會顯示一個初始 SVG 圖標的副本。

**就是這樣了!**看起來很棒,是吧?

你可能注意到了這個有趣的 xlink:href 屬性:這是你的實例與初始 SVG 之間的連接。

須要提到的是 xlink:href 是一個棄用的 SVG 屬性。儘管大多數瀏覽器仍然支持,你應該用 **href** 替代。如今的問題是,一些瀏覽器好比 Safari 不支持使用 href 進行 SVG 資源引用,所以你仍然須要提供 xlink:href 選項。

安全起見,兩個都用吧。

添加一些顏色

不像是字體, color 對於 SVG 圖標沒有任何做用:你必須使用 fill 屬性來定義一個顏色。這意味着他們將不會像圖標字體同樣繼承父文本顏色,可是你仍然能夠在 CSS 中定義它們的樣式。

// HTML
<svg class="icon">
  <use xlink:href="#my-first-icon" />
</svg>
// CSS
.icon {
  width: 100px;
  height: 100px;
  fill: red;
}
複製代碼

在這裏,你可使用不一樣的填充顏色建立同一個圖標的不一樣實例。

// HTML
<svg class="icon icon-red">
  <use xlink:href="#my-first-icon" />
</svg>
<svg class="icon icon-blue">
  <use xlink:href="#my-first-icon" />
</svg>
// CSS
.icon {
  width: 100px;
  height: 100px;
}
.icon-red {
  fill: red;
}
.icon-blue {
  fill: blue;
}
複製代碼

這樣就能夠生效了,可是不徹底符合咱們的預期。目前爲止,咱們全部作的事情可使用一個普通的圖標字體來實現。咱們想要的是在圖標的位置能夠有不一樣的顏色。咱們想要向每一個路徑上填充不一樣顏色,而不須要改變其餘實例,咱們想要可以在必要的時候重寫它。

首先,你可能會受到依賴於特性的誘惑。

// HTML
<svg xmlns="http://www.w3.org/2000/svg" style="display: none">
  <symbol id="my-first-icon" viewBox="0 0 20 20">
    <title>my-first-icon</title>
    <path class="path1" d="..." />
    <path class="path2" d="..." />
    <path class="path3" d="..." />
  </symbol>
</svg>
<svg class="icon icon-colors">
  <use xlink:href="#my-first-icon" />
</svg>
// CSS
.icon-colors .path1 {
  fill: red;
}
.icon-colors .path2 {
  fill: green;
}
.icon-colors .path3 {
  fill: blue;
}
複製代碼

不起做用。

咱們嘗試設置 .path1.path2.path3 的樣式,彷彿他們被嵌套在 .icon-colors 裏,可是嚴格來講,並不是如此<use> 標籤不是一個會被你的 SVG 定義替代的佔位符。這是一個引用將它所指向內容複製爲 shadow DOM 😱。

**那接下來咱們該怎麼辦?**當子項不在 DOM 中時,咱們如何才能用一個區域性的方式影響子項?

CSS 變量拯救世界

在 CSS 中,一些屬性從父元素繼承給子元素。若是你將一個文本顏色分配給 body ,這一頁中全部文本將會繼承那個顏色直到被重寫。父元素沒有意識到子元素,可是可繼承的樣式仍然繼續傳播。

在咱們以前的例子裏,咱們繼承了填充屬性。回頭看,你會看到咱們聲明填充顏色的類被附加在了實例上,而不是定義上。這就是咱們可以爲同必定義的每一個不一樣實體賦予不一樣顏色的緣由。

如今有個問題:咱們想傳遞不一樣顏色給原始 SVG 的不一樣路徑,可是隻能從一個 fill 屬性裏繼承。

這就須要 CSS 變量了。

就像任何其它屬性同樣, CSS 變量在規則集裏被聲明。你能夠用任意命名,分配任何有效的 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
<svg xmlns="http://www.w3.org/2000/svg" style="display: none">
  <symbol id="my-first-icon" viewBox="0 0 20 20">
    <title>my-first-icon</title>
    <path fill="var(--color-1)" d="..." />
    <path fill="var(--color-2)" d="..." />
    <path fill="var(--color-3)" d="..." />
  </symbol>
</svg>
<svg class="icon icon-colors">
  <use xlink:href="#my-first-icon" />
</svg>
// CSS
.icon-colors {
  --color-1: #c13127;
  --color-2: #ef5b49;
  --color-3: #cacaea;
}
複製代碼

而後… 生效了 🎉!

如今開始,爲了用不一樣的顏色方案建立實例,咱們所須要作的是建立一個新類。

// HTML
<svg class="icon icon-colors-alt">
  <use xlink:href="#my-first-icon" />
</svg>
// CSS
.icon-colors-alt {
  --color-1: brown;
  --color-2: yellow;
  --color-3: pink;
}
複製代碼

若是你仍然想有單色圖標,你沒必要在每一個 CSS 變量中重複一樣的顏色。相反,你能夠聲明一個單一 fill 規則:由於若是 CSS 變量沒有被定義,它將會回到你的 fill 聲明。

.icon-monochrome {
  fill: grey;
}
複製代碼

你的 fill 聲明將會生效,由於初始 SVG 的 fill 屬性被未設置的 CSS 變量值定義。

怎樣命名個人 CSS 變量?

當提到在 CSS 中命名,一般有兩條途徑:描述的或者語義的。描述的意思是告訴一個顏色是什麼:若是你存儲了 #ff0000 你能夠叫它 --red 。語義的意思是告訴顏色它將會被如何應用:若是你使用 #ff0000 來給一個咖啡杯把手賦予顏色,你能夠叫它 --cup-handle-color

描述的命名也許是你的本能。看起來更乾脆,由於#ff0000 除了咖啡杯把手還有更多地方能夠被使用。一個 --red CSS 變量可被複用於其餘須要變成紅色的圖標路徑。畢竟,這是實用主義在 CSS 中的工做方式。而且是一個良好的系統

問題是,在咱們的案例裏,咱們不能把零散的類應用於咱們想設置樣式的標籤。實用主義原則不能應用,由於咱們對於每一個圖標有單獨的引用,咱們不得不經過類的變化來設置樣式。

使用語義類命名,例如 --cup-handle-color ,對於這個狀況更有用。當你想改變圖標一部分的顏色時,你當即知道這是什麼以及須要重寫什麼。不管你分配什麼顏色,類命名將會一直關聯。

默認仍是不要默認,這是個問題

將你的圖標的多色版本設置成默認狀態是頗有誘惑力的選擇。這樣,你無需設置額外樣式,只須要在必要的時候能夠添加你本身的類。

有兩個方法能夠實現::rootvar() 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 變量,所以讓你不能複用同一個名字。

大多數時候,當一個圖標只用一種顏色,我用 --fill-color 名稱。簡單,易懂,對於全部僅須要一種顏色的圖標很是有意義。若是我必須在 :root 聲明中聲明全部變量,我就不會有幾個 --fill-color。我將會被迫定義 --fill-color-1--fill-color-2 或者使用相似 --star-fill-color--cup-fill-color 的命名空間。

var() 默認

你能夠用 var() 功能來把一個 CSS 變量分配給一個屬性,而且它的第二個參數能夠設置爲某個默認值。

<svg xmlns="http://www.w3.org/2000/svg" style="display: none">
  <symbol id="my-first-icon" viewBox="0 0 20 20">
    <title>my-first-icon</title>
    <path fill="var(--color-1, red)" d="..." />
    <path fill="var(--color-2, blue)" d="..." />
    <path fill="var(--color-3, green)" d="..." />
  </symbol>
</svg>
複製代碼

在你定義完成 --color-1--color-2--color-3 以前,圖標將會使用你爲每一個 <path> 設置的默認值。這解決了當咱們使用 :root 時的全局關聯問題,可是請當心:你如今有一個默認值,而且它將會生效。結果是,你不再能使用單一的 fill 聲明來定義單色圖標了。你將不得不一個接一個地給每一個使用於這個圖標的 CSS 變量分配顏色。

設置默認值會頗有用,可是這是一個折中方案。我建議你不要造成習慣,只在對給定項目有幫助的時候作這件事情。

How browser-friendly is all that?

CSS 變量與大多數現代瀏覽器兼容,可是就像你想的那樣, Internet Explorer 徹底不兼容。由於微軟要支持 Edge 終止了 IE11 開發, IE 之後也沒有機會遇上時代了。

如今,僅僅是由於一個功能不被某個瀏覽器(而你必須適配)兼容,這不意味着你必須全盤放棄它。在這種狀況下,考慮下優雅降級:給現代瀏覽器提供多彩圖標,給落後瀏覽器提供備份的填充顏色。

你想要作的是設置一個僅在 CSS 變量不被支持時觸發的聲明。這能夠經過設置備份顏色的 fill 屬性實現:若是 CSS 變量不被支持,它甚至不會被歸入考慮。若是它們不能被支持,你的 fill 聲明將會生效。

若是你使用 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;
  };
}
複製代碼

在 mixin 中經過 @content 傳遞 CSS 變量也是一個可選項。若是你在外面作這件事,被編譯的 CSS 將會變得同樣。可是它有助於被打包在一塊兒:你能夠在你編輯器中摺疊片斷而後用眼睛分辨在一塊兒的聲明。

在不一樣的瀏覽器中查看這個 pen 。在最新版本的 Firefox , Chrome 和 Safari 中,最後兩隻杯子各自擁有紅色杯身灰色煙氣和藍色杯身灰色煙氣。在 IE 和 版本號小於 15 的 Edge 中,第三個杯子的杯身與煙氣所有都是紅色,第四個則所有是藍色! ✨

若是你想了解更多關於 SVG 符號圖標(或者通常的 SVG ),我強烈建議你閱讀 Sara Soueidan 寫的一切東西。若是你有任何關於 CSS 符號圖標的問題,不要猶豫,儘管在 Twitter 上聯繫我。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索