用CSS給SVG 的內容新增樣式
轉自:http://www.w3cplus.com/svg/styling-svg-use-content-css.html
一篇深入探究如何給SVG
<use>
元素的內容新增樣式的文章,並針對碰到的問題逐一解決。
SVG圖形的一個最常見用例是圖示系統,其中最常用的SVG sprite技術就是使用SVG<use>
元素在文件中任意位置“例項化”圖示。
使用<use>
元素例項化圖示或任何其它的SVG元素或影象,給元素新增樣式時經常會碰到一些問題。這篇文章的目的是儘可能給你介紹一些方法來解決:使用<use>
引入的內容新增樣式受限的問題。
但是在開始之前,我們先快速瀏覽一下SVG的主要結構和分組元素,然後慢慢進入use
<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
屬性,以及用來給該元素定位的x
和y
屬性。你可以給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和普通的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屬性的簡寫方式。可以認為它們是特殊的樣式屬性。它們的目的就是給樣式級聯做貢獻,但是可能正走在一個我們不太期望的方向上。
在下面的程式碼片段中,簡單地展示了一個粉色的帶黃色描邊的圓。stroke
,stroke-width
和fill
都是描述屬性。
<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屬性),如opacity
和transform
,有一些還不行,如fill
、stroke
和stroke-width
。
在SVG 2中,這個列表將包括x
、y
、width
、height
、cx
、cy
,以及一些其它的描述屬性,目前還不能在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>
設定的樣式,像fill
、stroke
、stroke-width
、opacity
甚至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>
上同時使用fill
和color
屬性,然後利用currentColor
的變數特性,讓這些顏色級聯到<use>
的內容上。我們先看一個程式碼例項,看看這是怎麼搞的先。
假設我們要給這個小小的Codrops的logo新增兩種顏色的樣式——一個用於前面的水滴,一個用於後面的——logo的每一個例項都是採用兩種顏色。
首先,我們從上面的程式碼截圖開始:用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>
上使用fill
和color
屬性,給水滴新增樣式:
.codrops-1 {
fill: #4BC0A5;
color: #A4DFD1;
}
.codrops-2 {
fill: #0099CC;
color: #7FCBE5;
}
.codrops-3 {
fill: #5F5EC0;
color: #AEAFDD;
}
每個<use>
元素有自己的fill
和color
值。對單個水滴來說,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>