1. 程式人生 > >用CSS給SVG 的內容新增樣式

用CSS給SVG 的內容新增樣式

轉自:http://www.w3cplus.com/svg/styling-svg-use-content-css.html

一篇深入探究如何給SVG<use>元素的內容新增樣式的文章,並針對碰到的問題逐一解決。

svg use

SVG圖形的一個最常見用例是圖示系統,其中最常用的SVG sprite技術就是使用SVG<use> 元素在文件中任意位置“例項化”圖示

使用<use>元素例項化圖示或任何其它的SVG元素或影象,給元素新增樣式時經常會碰到一些問題。這篇文章的目的是儘可能給你介紹一些方法來解決:使用<use>引入的內容新增樣式受限的問題。

但是在開始之前,我們先快速瀏覽一下SVG的主要結構和分組元素,然後慢慢進入use

的世界中,以及shadow DOM,然後重回CSS的懷抱。我們會逐步講解為什麼給<use>內容新增樣式會比較麻煩,以及有什麼好的解決方案。

SVG結構化、分組,以及在SVG中引用(重用)元素速覽

SVG中有四個主要的元素用於在文件中定義、結構化和引用SVG程式碼。這些元素使得重用SVG元素變得容易,同時保持程式碼的簡潔性和可讀性。因為SVG的特性,這些元素和圖形編輯器中的某些命令具有相同的功能。

這四個用於SVG分組和引用的主要元素是:<g>, <defs>, <use><symbol>

<g>元素(“group”的簡寫),用於給邏輯上相聯絡的圖形元素分組。從圖形編輯器的角度,如Adobe Illustrator,<g>

元素提供了類似於分組物件的功能。你也可以把它想象成圖形編輯器中圖層的概念,因為一層也是一組元素。

當你想要應用某個樣式,並希望這個樣式能被組中的所有元素繼承,分組元素<g>非常好用,特別是當你想要給某組元素應用動畫,同時還需要保持它們彼此的空間關係的時候。

<defs>元素用來定義你之後要重用的元素。當你想要建立某一類在文件中要多次使用的“模板”時,使用<defs>定義元素。在<defs>元件中定義的元素不會在畫布中渲染出來,除非你在文件的某個位置呼叫了它們。

<defs>可以用於定義很多東西,但是最主要的使用情景之一是定義類似漸變的圖案,例如,使用這些漸變作為其它SVG元素的描邊填充。它可以用來定義你想要在畫布上渲染的任何元素。

<symbol>元素結合了<defs><g>元素的優點,將定義模板的元素組合在一起,以便之後在文件中的其他位置引用。和<defs>不同,<symbol>通常不用於定義圖案,但是經常用於定義例如圖示這樣的標誌,在整個文件中都可以被引用。

<symbol>元素相比其它兩個元素有一個非常重要的優點:它接受一個viewBox屬性,可以讓它在任何視窗中自適應大小縮放渲染。

<use>元素用於引用文件中其它位置定義的元素。你可以重用已有的元素,類似於圖形編輯器中的複製貼上功能。它可以重用單個元素,也可以重用一組用<g><defs><symbol>定義的元素。

要使用一個元素,你需要通過一個標識對該元素進行引用——一個ID,即use中的xlink:href屬性,以及用來給該元素定位的xy屬性。你可以給use元素應用樣式,這些樣式也會級聯應用到use元素的內容中去。

<use>的內容是什麼呢?它被克隆到哪裡了?CSS級聯如何處理這些內容?

在我們回答這些問題之前,因為我們目前只講了SVG結構化和分組元素,這裡還有幾篇值得我們繼續深入學習的文章,關於viewBox屬性和<symbol>的使用:

SVG <use>及shadow DOM

當你使用<use>引用元素時,程式碼如下:

<symbol id="my-icon" viewBox="0 0 30 30">
    <!-- icon content / shapes here -->
</symbol>

<use xlink:href="#my-icon" x="100" y="300" />

渲染在螢幕上的東西是內容定義在<symbol>內的圖示,但是這不是真正渲染出的內容,而是<use>的內容,也就是<symbol>內容的一個副本或者克隆。

但是<use>元素只是一個元素,它是自閉合的。在use標籤的開閉區間內沒有任何內容,所以<symbol>的內容是克隆到哪裡了呢?

答案是:Shadow DOM。(不知道為什麼,shadow DOM總是讓我想起蝙蝠俠(:зゝ∠)。)

什麼是shadow DOM?

shadow DOM和常規的DOM很類似,不同之處在於shadow DOM不是主文件子樹的一員,shadow DOM中的結點屬於文件片段,基本上等同於另一棵結點樹,不能像普通結點那樣新增指令碼和樣式。這給了作者們一種方法來封裝和包裹樣式及指令碼,當建立模組化元件時。如果你使用過HTML5的video元素,或range input型別,也很好奇video控制元件或者範圍輸入元件是從哪裡來的,那麼你就已經接觸過shadow DOM了。

在SVG<use>元素中,引用元素的內容被複制到一個文件片段中儲存,這個文件片段是由<use>保留著。<use>在這裡就是一個shadow Host。

所以,<use>的內容(克隆或複製那個它引用的元素的)都表示在一個shadow文件片段中。

也就是說,它們就在那裡,但是並不可見。就像普通的DOM內容一樣,但是並不是在“高等級”的DOM中,並不能在主文件中被CSS選擇器和JavaScript選中,它們被複制到由<use>保留的文件片段中。

現在,如果你是一個設計師,你可能會想:“ok,我瞭解了這東西了,但是有什麼方法可以檢查子文件,來看看它的真正的內容呢?”答案是:有的!你可以使用Chrome的開發者工具預覽shadow DOM的內容。(現在還無法在Firefox中檢視shadow DOM的內容。)但是為了完成這個,你需要先在“General”面板中勾選shadow DOM檢查的選項。也就是:開啟 Chrome 的開發者工具,點選右上角的“Settings”按鈕勾選“Show user agent shadow DOM”。

在開發者工具中勾選了shadow DOM檢查這一項之後,你可以在Elements面板中看到克隆的元素,和普通的DOM元素一樣。下面的圖片展示了<use>元素引用<symbol>元素的內容的示例。注意到有一個“#shadow-root”,而且當點開此片段的內容時——會發現它就是<symbol>內容的副本。

shadow DOM

檢查一下這些程式碼,你可以看到shadow DOM和普通的DOM非常相似,除了在主文件中用CSS和JavaScript處理時有不同的特性之外。它們之間還存在其它差異,但是這一節不可能完全在講shadow DOM,因為這真的是一個很大的概念,所以如果你想要閱讀和了解更多關於它的內容的話,我推薦下面這幾篇文章:

圖靈社群的同學翻譯的shadow DOM系列文章

對我來說,考慮如何限制和shadow DOM的互動時,我把它當成普通DOM一樣,除了在用CSS(和JavaScript)新增樣式時需要不同地處理。但是對於SVG開發者來說就是一個問題:shadow DOM中<use>的內容如何存在,當需要給內容應用樣式或者改變樣式的時候,因為我們希望可以為它們新增樣式。使用<use>的目的是為了可以建立某個元素的多個不同的“副本”,很多情境下,我們想要的是可以給差異化地給不同的副本新增樣式。例如,考慮一個有兩種樣式的logo(反轉顏色的主題)或多種顏色的icon,每一個都有自己的主題。這時,我們自然而然就會想到使用CSS來完成。

也就是說,我們前面提到的shadow DOM的內容在CSS看來不能像普通DOM一樣新增樣式。所以,我們要怎麼給它的內容新增樣式呢?我們不能像這樣指向<use>的路徑級聯:

use path#line {
    stroke: #009966;
}

因為我們不能使用普通的CSS選擇器來獲取shadow DOM。

有一組特殊的選擇器可以讓我們打破普通DOM的界限,給它裡面的結點應用樣式,但是這些選擇器並沒有很好的瀏覽器支援,而且相比CSS中提供的一長串用來選中普通DOM元素的選擇器,它們是受限的。

此外,我們希望有一個更簡單的方式來給SVG<use>的內容新增樣式,而不需要去接觸shadow DOM的具體內容——只使用簡單的CSS和SVG。

為了實現以及獲得更多一點控制,給<use>的內容新增樣式,我們需要從不同的角度思考,借用CSS級聯和繼承的優勢。

級聯樣式

因為SVG元素可以使用CSS通過三種不同的方法之一進行新增樣式:外部的CSS樣式(在外部的CSS檔案中),內部樣式塊(<style>元素包裹),以及內聯樣式(在元素的style屬性中)。重點在於這些級聯管理是如何將樣式應用到元素之上的。

除了CSS屬性,SVG元素還可以使用描述屬性新增樣式。描述屬性是在元素上設定CSS屬性的簡寫方式。可以認為它們是特殊的樣式屬性。它們的目的就是給樣式級聯做貢獻,但是可能正走在一個我們不太期望的方向上。

在下面的程式碼片段中,簡單地展示了一個粉色的帶黃色描邊的圓。strokestroke-widthfill都是描述屬性。

<svg viewBox="0 0 100 100">
    <circle fill="deepPink" stroke="yellow" stroke-width="5" cx="50" cy="50" r="10"></circle>
</svg>

在SVG中,所有CSS屬性的子集可以通過SVG屬性設定,反之亦然。這意味著,不是所有的CSS屬性都可以被指定給SVG元素作為描述屬性,也不是所有SVG支援的描述屬性都可以在CSS中指定,雖然有很多都可以。

SVG規範列出了可以設定為CSS屬性的SVG屬性。其中一些屬性可以和CSS共享(也就是已經可以作為CSS屬性),如opacitytransform,有一些還不行,如fillstrokestroke-width

在SVG 2中,這個列表將包括xywidthheightcxcy,以及一些其它的描述屬性,目前還不能在SVG 1.1中通過CSS來設定的。新的屬性列表可以在SVG 2規範中找到。

如果你和我一樣,那麼你一定會期待描述屬性可以有相比其它樣式宣告更高的特殊性。我的意思是,畢竟,外部的樣式可以被內部的樣式塊覆蓋,內部的樣式塊又可以被style屬性設定的內聯樣式覆蓋。那麼這看起來是不是越“內層”的樣式,優先順序就越高。所以如果一個屬性有了自己的特性,它是不是就更強大,因此它也就可以覆蓋所有其它的樣式宣告。儘管這對我來說是非常棒的,但是它真正的工作原理卻不是這樣的。

事實上,描述屬性算是比較低層級的“作者樣式層疊表”,可以被其它所有的樣式定義覆蓋:外部的樣式表,內部的樣式塊以及內聯樣式。描述屬性唯一超過的就是繼承樣式。就是說,描述屬性只可以覆蓋文件中的繼承樣式,但是會被其它所有的樣式宣告覆蓋(:зゝ∠)

好滴~既然我們現在弄清楚了,我們回到<use>元素以及它的內容上吧。

我們現在知道我們不同使用CSS選擇器給<use>中的元素設定樣式。

我們知道,正如<g>元素,你應用給<use>的樣式將會被它所有的後代內容繼承(也就是shadow DOM中的內容)。

所以第一個改變<use>內元素的fill顏色的嘗試就是給<use>元素本身應用此填充顏色,並讓其繼承和級聯。

但是,這帶來了兩個問題:

  • 該填充顏色將被<use>的所有後代內容繼承,甚至包括那些你並不想給它們應用樣式的內容(如果你的<use>中還沒有任何元素,那麼這就不成問題。)
  • 如果是通過圖形編輯器匯出,或者是從其它設計師手中拿到的SVG,簡單來說,就是你不能接觸到SVG原始碼,那麼你可能就沒辦法給SVG元素應用描述屬性了(除非你明確指出你不希望在輸出SVG的時候發生這個事情,但這是另一個話題了),這些屬性的值將覆蓋你給<use>應用的所有樣式。現在,我假設如果你給<use>指定了樣式,而且希望這些樣式可以被它的後代繼承,那麼描述屬性可能會給你帶來不便。

即使你可以獲取SVG的程式碼,你也可以擺脫描述屬性,我強烈建議不要這樣做,因為:

  • 刪除那些用於設定某些屬性的特性(:зゝ∠),將會導致這些屬性被重置為初始的瀏覽器預設值——也就是,一般情況下,所有都是黑色填充和描邊(比如我們現在討論的是顏色)。
  • 通過重置所有值,你可以強迫自己去給所有屬性集指定樣式,所以除非你想這麼做,否則你不要想擺脫這些描述屬性了。
  • 描述屬性設計的初衷是作為一項降級機制,用於當你的外部樣式因為某些原因不能應用的時候。如果CSS因為某些東西給搞砸而不能載入的時候,你的圖示至少可以有些預設的相對漂亮的樣式可以降級。這點我強烈建議保留它們。

好了,現在我們有這些屬性了,但是我們還想針對不同的例項應用不同的樣式,比如說,不同的圖示。

需要做的就是確保我們強制描述屬性繼承了設置於<use>之上的樣式,或者找到一個方法來讓它們覆蓋這些值。為了做到這一點,我們需要利用CSS的優勢。

我們從最簡單的例項開始,然後慢慢進入到更復雜的情景。

CSS描述屬性值的介紹

描述屬性可以被其它任何的樣式宣告覆蓋。我們可以利用這個優勢,用一個外部的樣式宣告,強制描述屬性覆蓋從use繼承的值。

通過使用CSSinherit關鍵字,這會變得非常簡單。看看下面的例子,我們繪製了一個冰淇淋的圖示,只用一條路徑完成,而且可以根據不同的情況改變填充顏色。這個圖示是Erin Agnoli在Noun專案中建立的。

<svg>
  <symbol id="ic">
    <path fill="#000" d="M81,40.933c0-4.25-3-7.811-6.996-8.673c-0.922-5.312-3.588-10.178-7.623-13.844  c-2.459-2.239-5.326-3.913-8.408-4.981c-0.797-3.676-4.066-6.437-7.979-6.437c-3.908,0-7.184,2.764-7.979,6.442  c-3.078,1.065-5.939,2.741-8.396,4.977c-4.035,3.666-6.701,8.531-7.623,13.844C22.002,33.123,19,36.682,19,40.933  c0,2.617,1.145,4.965,2.957,6.589c0.047,0.195,0.119,0.389,0.225,0.568l26.004,43.873c0.383,0.646,1.072,1.04,1.824,1.04  c0.748,0,1.439-0.395,1.824-1.04L77.82,48.089c0.105-0.179,0.178-0.373,0.225-0.568C79.855,45.897,81,43.549,81,40.933z   M49.994,11.235c2.164,0,3.928,1.762,3.928,3.93c0,2.165-1.764,3.929-3.928,3.929s-3.928-1.764-3.928-3.929  C46.066,12.997,47.83,11.235,49.994,11.235z M27.842,36.301c0.014,0,0.027,0,0.031,0c1.086,0,1.998-0.817,2.115-1.907  c0.762-7.592,5.641-13.791,12.303-16.535c1.119,3.184,4.146,5.475,7.703,5.475c3.561,0,6.588-2.293,7.707-5.48  c6.664,2.742,11.547,8.944,12.312,16.54c0.115,1.092,1.037,1.929,2.143,1.907c2.541,0.013,4.604,2.087,4.604,4.631  c0,1.684-0.914,3.148-2.266,3.958H25.508c-1.354-0.809-2.268-2.273-2.268-3.958C23.24,38.389,25.303,36.316,27.842,36.301z   M50.01,86.723L27.73,49.13h44.541L50.01,86.723z"/>
  </symbol>
</svg>

這個冰淇淋圖示的內容(也就是path)是定義在一個<symbol>元素中的,也就是說它們不會直接在SVG畫布中渲染。

然後,我們使用<use>渲染出多個圖示例項。

<svg class="icon" viewBox="0 0 100 125"> 
    <use class="ic-1" xlink:href="#ic" x="0" y="0" />
</svg>
<svg class="icon" viewBox="0 0 100 125"> 
    <use class="ic-2" xlink:href="#ic" x="0" y="0" />
</svg>

我們在CSS中設定圖示的寬度和高度。我使用了viewBox一樣的尺寸,但它們也不是一定要相同的。但是,為了避免SVG內多餘的空白太多,保持它們的寬高比。

.icon {
    width: 100px;
    height: 125px;
}

使用上面的程式碼,你可以得到下面的結果:

注意我給SVG添加了一個黑色的邊框,這樣大家才可以看到每個圖的邊界,我們定義的第一個SVG圖示的內容並沒有渲染。這裡可以提出一點:你在symbol中定義的SVG文件也會在頁面中渲染出來,即使它沒有包括渲染圖形。為了避免這一點,確保你在第一個SVG中設定了display: none。如果你沒有隱藏包含圖示定義的SVG,即使你沒有明確設定任何尺寸,它也會被渲染出來——瀏覽器預設尺寸是300x150px,這是在CSS中沒有替代元素時的預設尺寸,所以你會在頁面上得到一塊白色的區域,儘管你並不想要這塊東西。

現在讓我們試試改變每個圖示例項的填充顏色:

use.ic-1 {
    fill: skyblue;
}
use.ic-2 {
    fill: #FDC646;
}

即使這樣寫了,圖示的填充顏色還是不會有任何改變,因為繼承的顏色值被path元素的fill="#000"覆蓋了。為了阻止這個東西,我們強制讓path繼承顏色值:

svg path {
    fill: inherit;
}

瞧!我們給<use>元素設定的顏色現在可以逐個應用於path了。檢視下面的demo,可以照自己喜歡的去改變顏色值,建立更多例項:

現在這種技術已經非常好用,當你想要強制<use>的內容繼承你設定的樣式時。但是在大多數情況下,這可能不是你想要的。還有很多其它新增樣式的場景,所以我們接下來會介紹一些其它的方法。

使用CSS的all屬性給<use>的內容新增樣式

前段時間我使用一個引用自use的圖示,我想讓它裡面的其中一個元素可以繼承所有我給<use>設定的樣式,像fillstrokestroke-widthopacity甚至transform。基本上,我希望可以控制所有這些CSS屬性,同時保留標籤中的描述屬性作為降級。

如果你發現你也處在這樣一個場景中,你可能會發現這用CSS做起來非常耗時間:

path#myPath {
    fill: inherit;
    stroke: inherit;
    stroke-width: inherit;
    transform: inherit;
    /* ... */
}

看看上面的程式碼片段,你可以看到都是同一個模式,我們應該可以把所有這些屬性結合起來,放到一個屬性中,並把所有這些屬性的值設定為inherit

幸運的是,這就是CSS的all屬性發光發熱的時候了。我之前寫過關於使用all屬性來給SVG的<use>內容新增樣式的參考條目,但是因為我們現在的上下文環境,我們需要再看看。

使用all屬性,我們可以這樣寫:

path#myPath {
    all: inherit;
}

這在所有支援all屬性的瀏覽器中都工作得非常好(詳細資訊請檢視屬性參考條目),然而還有幾個重點要記住:這條宣告會真正地給元素的所有屬性都設定從父元素繼承值,包括那些你可能不想要的屬性。所以除非你想要在CSS中給元素的所有屬性都設定樣式,否則你就不要使用它——這是一種極端的措施,當你想要暴露你的元素,然後在CSS中對它的樣式屬性進行完全的控制的時候才使用,這種情況比較少見。如果你使用這條宣告,不在CSS中指定所有屬性的值,它們就會直接往上然後級聯,知道它們找到可以繼承的值,大多數情況下就是瀏覽器的預設樣式,從預設使用者代理樣式表載入而來。

注意這隻會影響到那些可以在CSS中設定的屬性,不包括那些SVG獨有的屬性。所以如果一個屬性可以作為CSS屬性設定,它就會被設定為inherit,否則就不會。

能夠強制描述屬性去從<use>繼承樣式是強大的,但是如果你的圖示是由多個元素組成的呢,你肯定不想要讓所有的這些元素都從use繼承同一個fill顏色吧?那如果你想要給不同的use級聯應用多個填充顏色怎麼辦呢?給use設定一個樣式已經不足夠了,我們需要一些其它的東西來幫助我們從正確的元素級聯正確的顏色。

使用CSS的currentColor變數來給<use>內容新增樣式

使用CSS的currentColor變數,並結合上面的技術,我們可以給一個元素指定兩種不同的顏色,而不僅是一種。Fabrice Weinberg在他的CodePen blog寫了一些關於這種技術的文章

這種技術的內幕其實是在<use>上同時使用fillcolor屬性,然後利用currentColor的變數特性,讓這些顏色級聯到<use>的內容上。我們先看一個程式碼例項,看看這是怎麼搞的先。

假設我們要給這個小小的Codrops的logo新增兩種顏色的樣式——一個用於前面的水滴,一個用於後面的——logo的每一個例項都是採用兩種顏色。

currentColor

首先,我們從上面的程式碼截圖開始:用symbol包裹我們的圖示定義,然後使用三個<use>建立三個logo例項。

<svg style="display: none;">
<symbol id="codrops" viewBox="0 0 23 30">
    <path class="back" fill="#aaa" d="M22.63,18.261c-0.398-3.044-2.608-6.61-4.072-9.359c-1.74-3.271-3.492-5.994-5.089-8.62l0,0   c-1.599,2.623-3.75,6.117-5.487,9.385c0.391,0.718,0.495,1.011,0.889,1.816c0.143,0.294,0.535,1.111,0.696,1.43   c0.062-0.114,0.582-1.052,0.643-1.162c0.278-0.506,0.54-0.981,0.791-1.451c0.823-1.547,1.649-2.971,2.469-4.33   c0.817,1.359,1.646,2.783,2.468,4.33c0.249,0.47,0.513,0.946,0.791,1.453c1.203,2.187,2.698,4.906,2.96,6.895   c0.292,2.237-0.259,4.312-1.556,5.839c-1.171,1.376-2.824,2.179-4.663,2.263c-1.841-0.084-3.493-0.887-4.665-2.263   c-0.16-0.192-0.311-0.391-0.448-0.599c-0.543,0.221-1.127,0.346-1.735,0.365c-0.56-0.019-1.095-0.127-1.599-0.313   c1.448,3.406,4.667,5.66,8.447,5.78C19.086,29.537,23.469,24.645,22.63,18.261z"/>
    <path class="front" fill="#ddd" d="M6.177,11.659c0.212,0.367,0.424,0.747,0.635,1.136c0.164,0.303,0.333,0.606,0.512,0.927   c0.683,1.225,1.618,2.898,1.755,3.937c0.144,1.073-0.111,2.056-0.716,2.769c-0.543,0.641-1.315,1.014-2.186,1.067   c-0.87-0.054-1.643-0.43-2.186-1.067c-0.604-0.713-0.858-1.695-0.715-2.771c0.137-1.036,1.072-2.712,1.755-3.936   c0.18-0.32,0.349-0.623,0.513-0.927C5.752,12.404,5.964,12.026,6.177,11.659 M6.177,5.966L6.177,5.966   c-1.02,1.649-2.138,3.363-3.247,5.419c-0.932,1.728-2.344,3.967-2.598,5.88c-0.535,4.014,2.261,7.09,5.846,7.203   c3.583-0.113,6.379-3.189,5.845-7.203c-0.255-1.912-1.666-4.152-2.598-5.88C8.314,9.329,7.196,7.617,6.177,5.966L6.177,5.966z"/>
</symbol>
</svg>
<svg height="90px" width="69px">
  <use xlink:href="#codrops" class="codrops-1"/>
</svg>
<svg height="90px" width="69px">
  <use xlink:href="#codrops" class="codrops-2"/>
</svg>
<svg height="90px" width="69px">
  <use xlink:href="#codrops" class="codrops-3"/>
</svg>

如果我們想要給<use>元素的每個例項設定fill顏色,該顏色將會兩條路徑都繼承,最後它們就會有相同的顏色——這不是我們想要的。

所以我們不僅要指定fill顏色,還要讓它按照預設方法級聯,然後使用currentColor變數來確保icon前面的小水滴獲取和背景不同的顏色值:該值通過color屬性指定。

首先,我們需要在我們想要應用該顏色值的地方插入currentColor;進入定義圖示內容的標籤,在<symbol>中。程式碼如下:

<svg style="display: none;">
    <symbol id="codrops" viewBox="0 0 23 30">
        <path class="back" fill="#aaa" d="..."/>
        <path class="front" fill="currentColor" d="..."/>
    </symbol>
</svg>

下一步我們需要從另一個水滴中刪除fill描述屬性,並讓它從use繼承fill顏色,而不是使用inherit技術。

如果我們要使用inherit關鍵字來強制描述屬性從use繼承值,兩條路徑都會繼承相同的值,這樣currentColor就不會再產生任何效果。所以,在這種技術中,我們需要刪除我們想要在CSS中設定的屬性,只保留那個我們想要使用currentColor設定的值。

<svg style="display: none;">
    <symbol id="codrops" viewBox="0 0 23 30">
        <path class="back" d="..."/>
        <path class="front" fill="currentColor" d="..."/>
    </symbol>
</svg>

現在,在<use>上使用fillcolor屬性,給水滴新增樣式:

.codrops-1 {
  fill: #4BC0A5;
  color: #A4DFD1;
}
.codrops-2 {
  fill: #0099CC;
  color: #7FCBE5;
}
.codrops-3 {
  fill: #5F5EC0;
  color: #AEAFDD;
}

每個<use>元素有自己的fillcolor值。對單個水滴來說,fill顏色級聯並被沒有fill屬性的第一條路徑(後面的水滴)繼承,color屬性的值被作為第二條路徑(前面的水滴)的fill屬性的值。

所以當前顏色值被引用到<use>元素裡邊,使用currentColor。漂亮整潔,對嗎?

這是上面程式碼的demo:

這種雙色變數技術對於簡單的雙色標誌非常好用。在Fabrice的文章中,他建立了三個不同的sass logo,通過改變文字的顏色和背景顏色。

currentColor關鍵字目前只在CSS變數中支援。但是,如果我們有更多的變數,是不是就可以分配和釋放更多的值到<use>內容中呢?對的!Amelia Bellamy-Royds在CodePen blog的文章中介紹了這個概念,大概一年多之前。我們來看看它是如何工作的。

將來:使用CSS自定義屬性給<use>內容新增樣式,即CSS變數

使用CSS自定義屬性(即CSS變數),你可以給<use>的內容新增樣式,而不需要強制瀏覽器覆蓋任何描述屬性的值。

MDN中定義的,CSS變數可以是作者、或使用者,定義的,web頁面中包含整個文件中指定的值的實體。它們被設定了使用自定義屬性,並通過一個指定的功能符號var()訪問。和CSS前處理器(如sass)的變數非常相似,但是更靈活,可以做前處理器變數不能做的事情。(CSS變數很快將被新增到Codrops CSS參考條目中,敬請期待。)

變數,即CSS變數或前處理器變數,都可以有很多使用示例,但是主題(顏色)是最常見的用例之一。在這一節中我們將講解在給SVG新增樣式時如何使用它。

我們先從一張symbol中定義並通過use例項化的影象開始,並且只為這張影象應用這種技術;只要你想,這個示例中給<use>內容應用樣式的概念,也可以被應用到很多<use>元素中。

現在,假設我們有如下這個可愛時髦的機器人插畫,Freepik設計~

機器人插畫

機器人的程式碼包括了這些組成顏色。

<svg style="display: none">
    <symbol id="robot" viewBox="0 0 340 536">
        <path d="..." fill="#fff" />
        <path d="..." fill="#D1312C" />
        <path d="..." fill="#1E8F90" />
        <path d="..." fill="#1E8F90" />
        <path d="..." fill="#fff" />
        <path d="..." fill="#6A4933" />
        <path d="..." fill="#1E8F90"  />
        <path d="..." fill="#6A4933" />
        <path d="..." fill="#fff" />
        <path d="..." fill="#6A4933" />
        <path d="..." fill="#F2B42B" />
        <path d="..." fill="#fff" />

       <!-- rest of the shapes -->
    </symbol>
</svg>

現在,我們不打算使用CSS變數作為每條路徑的fill屬性的值;我們將使用CSSfill屬性作為其填充顏色值,並保留原位置的fill屬性。這個屬性將作為降級使用,在不支援CSS變數的瀏覽器中,這樣這個影象在那些不支援變數的瀏覽器中,仍然能保留初始樣式。

添加了變數之後,上面的程式碼變成如下:

<svg style="display: none">
    <symbol id="robot" viewBox="0 0 340 536">
        <path d="..." fill="#fff" />
        <path d="..." fill="#D1312C" />
        <path d="..." fill="#1E8F90" style="fill: var(--primary-color)" />
        <path d="..." fill="#1E8F90" style="fill: var(--primary-color)" />
        <path d="..." fill="#fff" />
        <path d="..." fill="#6A4933" style="fill: var(--tertiary-color)" />
        <path d="..." fill="#1E8F90" style="fill: var(--primary-color)" />
        <path d="..." fill="#6A4933" style="fill: var(--tertiary-color)" />
        <path d="..." fill="#fff" />
        <path d="..." fill="#6A4933" style="fill: var(--tertiary-color)" />
        <path d="..." fill="#F2B42B" style="fill: var(--secondary-color)" />
        <path d="..." fill="#fff" />

        <!-- rest of the shapes -->
    </symbol>
</svg>

因為內聯style標籤會覆蓋描述屬性,支援CSS變數的瀏覽器會使用這些變數作為圖形的填充顏色。不支援CSS變數的瀏覽器將使用fill屬性值。

下一步,我們需要在CSS中定義變數的值。首先,插畫需要使用use例項化:

<svg width="340" height="536">
    <use xlink:href="#robot" id="robot-1" />
</svg>