天天看點

手把手教你用 SVG 符号和 CSS 變量做出彩色圖示

手把手教你用 SVG 符号和 CSS 變量做出彩色圖示

譯者 | KenChoi

作者 | Sarah Dayan

原文 | https://medium.freecodecamp.org/lets-make-your-svg-symbol-icons-multi-colored-with-css-variables-cddd1769fca4

使用圖檔和 CSS 樣式來制作網頁圖示的日子已經過去了,随着網頁字型的爆發,圖示字型成為展示圖示的第一解決方案。

字型都是向量,是以你不必擔心分辨率的問題。它們能作為文本來用 CSS 屬性修飾,是以你能夠完全掌控它們的大小,顔色以及樣式。還可以添加變換,效果以及裝飾比如旋轉,下劃線或陰影。

手把手教你用 SVG 符号和 CSS 變量做出彩色圖示

然而圖示字型還不夠完美,這就是為什麼越來越多的人使用内嵌 SVG 圖檔的原因。CSS Tricks 寫了一個圖示字型和原生的 SVG 元素相比的諸多缺點:清晰度,位置,因為受到跨域加載,浏覽器相容,以及廣告攔截器等因素影響甚至稱得上失敗。現在通過一個安全的選擇來制作圖示字型完全可以避免大多數這種問題。

然而,還有一個圖示字型絕對不能做到的問題:多顔色支援。隻有 SVG 能做到。

設定 SVG 符号圖示

内嵌的 SVG 的問題在于它們的備援。你肯定不想在每次使用同一個圖示時複制粘貼所有的坐标。這顯得笨拙,可讀性差,難以維護。

使用 SVG 符号圖示,你隻要複制整個 SVG 元素一次,然後你就可以用一個引用在任何地方執行個體化它們。

從内嵌 SVG 開始,隐藏它,并用一個 <symbol> 包裹,然後加上 id 屬性:

<svgxmlns="http://www.w3.org/2000/svg"style="display: none"><symbolid="my-first-icon"viewBox="0 0 20 20"><title>my-first-icon</title><pathd="..."/></symbol></svg>      

一次包含完整的 SVG 标記并隐藏在 HTML 裡。

然後,隻要用 <use> 元素來執行個體化這個圖示就行了:

<svg>
  <usexlink:href="#my-first-icon"/></svg>      

這樣就可以展示你之前的 SVG 圖示了:

手把手教你用 SVG 符号和 CSS 變量做出彩色圖示

就是辣麼簡單!對吧?

你可能注意到了有意思的 ​

​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;}      

這會起作用,但不是我們确切想要的。到這裡,我們剛才完成的步驟可以得到一個正常的圖示字型。我們想要的是對于圖示的每個部分可以有不同的顔色。我們想要用不同的顔色填充每個 path 而不用更改其他執行個體,必要的時候可以重寫。

一開始,你可能會想到用特殊性:

// 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 的占位符。它是一個引用,複制了指向暗處的 DOM 的内容。😱

那我們該怎麼做?我們怎樣才能影響不在 DOM 中的子元素?

CSS 變量來幫忙

在 CSS 中,一些屬性從祖先到孩子都繼承了。如果你配置設定給 ​

​body​

​ 配置設定一個文本顔色,所有頁面中的文本都會繼承這個顔色,除非顔色被重寫。祖先不知道孩子,但可繼承的樣式被傳遞下來。

我們開始的示例中,繼承了 ​

​fill​

​​ 屬性。再看一次,你會看到我們聲明的 ​

​fill​

​顔色被添加到執行個體中,而不是定義的 SVG。這就是我們能夠為每個執行個體添加不同顔色的原因。

現在問題來了:我們想要将不同的顔色傳遞給原始 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;}      

然後……大功告成!🎉

手把手教你用 SVG 符号和 CSS 變量做出彩色圖示

從現在開始,我們隻需要用不同的顔色的 CSS 類建立不同的執行個體。

// 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​

​​ 聲明會起作用因為 ​

​fill​

​ 屬性的值在原始的 SVG 上是沒有定義的 CSS 變量。

我的 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 變量,進而避免你使用相同的名字。

大多數時候,當一個圖示僅使用一種顔色,我會用 ​

​--fill-color​

​​ 。它簡單易懂,當你隻想用一種顔色填充所有的圖示時,這種方式也比較行得通。如果我必須在 ​

​:root​

​​ 中聲明所有的變量,我不能有多個 ​

​--fill-color​

​​ 。我必須定義 ​

​--fill-color-1​

​​, ​

​--fill-color-2​

​​等等,或者使用命名空間比如 ​

​--star-fill-color​

​​, ​

​--cup-fill-color​

​。

var() default

用來将 CSS 變量配置設定給某個屬性的 ​

​var()​

​ 函數可以将第二個參數作為預設值。

<svgxmlns="http://www.w3.org/2000/svg"style="display: none">
  <symbolid="my-first-icon"viewBox="0 0 20 20">
    <title>my-first-icon</title>
    <pathfill="var(--color-1, red)"d="..."/>
    <pathfill="var(--color-2, blue)"d="..."/>
    <pathfill="var(--color-3, green)"d="..."/>
  </symbol></svg>      

除非你定義 ​

​--color-1​

​​,​

​--color-2​

​​ 和 ​

​--color-3​

​​, 否則圖示會使用預設的值為每個 ​

​<path>​

​​ 填充顔色。這解決了之前使用 ​

​:root​

​​ 時全局範圍的問題,但是要注意:你現在使用了預設值,并且它正在生效。結果是,你不能夠使用單個 ​

​fill​

​ 聲明來定義單色的圖示了。你必須為每個 CSS 變量配置設定顔色,一個一個地。

設定預設值很有用,但它是種權衡。我建議你不要将這種做法成為一種習慣,隻有當它在特定的項目有意義時才使用。

浏覽器相容

CSS 變量對于絕大多數現代浏覽器都是相容的,但正如你所想的那樣,IE 浏覽器完全不支援。甚至 IE 11 也不支援,自從 Edge 的開發被擱置後,它再也沒機會趕上了。

現在,僅僅因為一個浏覽器不支援這個功能,這并不意味着你要推翻一切來迎合它。這種情形有更加優雅的做法:為現代浏覽器提供多彩圖示,為其他的老版本浏覽器提供復原的填充顔色。

你想要做的是當 CSS 變量不支援時設定一個會起作用的聲明,這可以通過為 ​

​fill​

​​ 屬性設定復原顔色來實作:當支援 CSS 變量時,它不會生效,如果不支援,你的 ​

​fill​

​​ 聲明才會生效。如果你使用 Sass,可以抽象到​

​@mixin​

​ 裡:

@mixinicon-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;
  };}      

本文完~

手把手教你用 SVG 符号和 CSS 變量做出彩色圖示