一次搞懂SVG中所有常用的濾鏡

蘇桓晨
13 min readApr 21, 2019

本文將介紹SVG濾鏡原理以及應用方式,使設計師們能夠輕易上手濾鏡並製作互動性SVG。包含:feFlood, feBlend, feDisplacementMa, pfeTurbulence, feColorMatrix, feImage, feGaussian​Blur, pattern

濾鏡的使用方法

建立一個標籤<filter>並放在<defs>中,在形狀標籤中用屬性filter=”url(#myFilter)”即可,我們先放一個feFlood當例子:

<svg>
<defs>
<filter id="flood">
<feFlood flood-color="#00ff00"/> //濾鏡只有一個feFlood
</filter>
</defs>
<rect height="100" width="100" filter="url(#flood)" />
</svg>

<feFlood>

想像成油漆桶工具,將整個畫面填滿某個顏色,還能加上透明度。

<feFlood flood-color="#00ff00" flood-opacity="0.6" />

結果

濾鏡可以同時用很多效果

設計師能夠放一個濾鏡,也可以放好多個。

比較需要考慮的是,大部分濾鏡需要圖像的輸入,我們以feBlend當例子,feBlend本身等等會介紹:

<filter>
<feFlood flood-color="#00ff00" result="myFlood"/>
<feBlend in="SourceGraphic" in2="myFlood" mode="screen"/>
</filter>
<rect height="100" width="100" filter="url(#flood)" />

輸入什麼?輸入濾鏡要合成的圖像。拿攝影當例子,攝影上我們會用一張濾鏡在鏡頭前面來回刷,攝影機捕捉鏡頭前面的濾鏡下的風景,濾鏡與風景兩個都被鏡頭捕捉,同樣的,我們需要告訴SVG,誰是那片濾鏡,誰是風景。在上面feBlend中,in指定SourceGraphic,即是使用濾鏡的<rect>當做風景;in2即是上一個標籤feFlood當做濾鏡。

有些濾鏡不用來源輸入來源,如<feFlood>,有些需要兩個輸入來源,使用in=“#id”, in2=“#id”定義,有些需要兩個輸入來源但可以省略第二個。依照濾鏡的要求而定。

像是feBlend,混合模式,需要兩個輸入來源,設計師可以幫濾鏡設ID,但在輸入來源中,以result屬性當做輸入來源名稱:

如果省略in2,那它將自動以上一個濾鏡的結果當做in2。

<feBlend>

回來feBlend,想像成修圖軟體的混合模式:

主要屬性有三個,in (輸入來源)、in2 (輸入來源二)、mode (混合模式)。

它需要兩個輸入來源,並設定模式,即可使用。如果in2被省略,它將以上一個標籤當做in2:

<filter id="floodAndBlend">
<feFlood flood-color="#0000dd"/>
<feBlend in="SourceGraphic" mode="screen" />
</filter>
<rect fill="#dd00dd" filter="url(#floodAndBlend)" />
圖例

mode有幾種模式normal, multiply, darken, screen, lighten,參考修圖軟體中修圖模式的「一般」、「色彩增值」、「暗化」、「網屏」、「亮化」

<feTurbulence>

想像是一種雜訊,但不是粒子狀的雜訊,是RGBA都雜訊的圖像。單純使用這個濾鏡沒什麼好玩的,通常會套用在等等會提的feDisplacementMap一啟用,建立皺折、雜訊、花玻璃等效果。

主要屬性有五種,baseFrequency (想像雜訊圖檔大小 0.01~1)、numOctaves (想像精緻度 1~10)、seed (雜訊圖檔種子碼)、stitchTiles (雜訊圖檔是否拼接)、type (類型,fractalNoise較”霧面”,turbulence較顆粒)

<filter id="feTurbulence">      
<feTurbulence
type="turbulence"
baseFrequency="0.9"
numOctaves="2"
result="turbulence" />
</filter>
<circle fill="#ff0000" r="100" filter="url(#feTurbulence)" />

結果:

<feDisplacementMap>

用神奇的運算方法,對輸入來源1每個像素位置應用輸入來源2的顏色移置,來源1的圖片會因為來源2的顏色產生偏移。至於哪個顏色、垂直還是左右偏,則用ChannelSelector定義。

實作這張圖的部份可參考Gabi的文章

同樣有五個屬性,in (要被影響圖像1)、in2 (要發揮影響的圖像2)、scale(幅度)、xChannelSelector (X座標要用RGBA什麼顏色)、yChannelSelector (Y座標要用RGBA什麼顏色)。

本文以feTurbulence來配合:

<filter id="feDisplacementMapAndfeTurbulence">   <feTurbulence 
type="turbulence"
baseFrequency="0.05"
numOctaves="1"
type="fractalNoise"
result="turbulence" />
<feDisplacementMap
in="SourceGraphic"
in2="turbulence"
scale="50"
xChannelSelector="R"
yChannelSelector="G" />
</filter><circle fill="#ff0000" r="100" filter="url(#feDisplacementMapAndfeTurbulence)" />

如此就能透過feTurbulence的雜訊來影響circle:

如果我們使用放射性漸層當做移置的根據也行。

但比較奇怪的是在用feComposite合併紅色放射漸層與狗狗照片時,SourceGraphic的擺放位置有限制。當SourceGraphic沒有在in或是in2時沒有作用,只有SourceGraphic放在in2才成功。

要製作的話先作一個漸層物件,再套用漸層物件於指定形狀,將形狀再套用有背景圖片當in、SourceGraphic當in2的濾鏡即可。

<defs>  <radialGradient id="radiation-set" cx=".5" cy=".5" r=".8">
<stop offset="0" stop-color="#f00"></stop>
<stop offset=".1" stop-color="#000"></stop>
<stop offset=".2" stop-color="#f00"></stop>
<stop offset=".3" stop-color="#000"></stop>
<stop offset=".4" stop-color="#f00"></stop>
<stop offset=".5" stop-color="#000"></stop>
</radialGradient>
<filter id="ripple-effect">
<feImage xlink:href="./myDog.jpg" result="dogFilter"/>
<feDisplacementMap
in2="SourceGraphic" in="dogFilter" scale="15"
xChannelSelector="R" yChannelSelector="G" />
</filter>
</defs><rect x="0" y="0" width="200" height="200" fill="url(#radiation-set)" filter="url(#ripple-effect)"></rect>
(註:圖檔屬於https://codepen.io/enxaneta/post/svg-waves-with-fedisplacementmap並非我擁有)

<feColorMatrix>

顏色的調校,原本像素的RGBA通道各乘上不同數值之後輸出。數值為一組陣列。需要三個屬性:in (來源圖檔)、type(濾鏡本身提供的顏色調整類型)、values (需要乘上的數值,是陣列)

<feColorMatrix type="matrix" values="1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0" />

透過這個陣列的數值,將可以製作整體顏色的調整,這陣列很簡單不用擔心,橫的是原本圖片的顏色RGBA再加一個常數,其中A是透明度的意思;垂直是圖片顏色的結果。假如一個全部是紅色的像素在陣列中被乘上了1,最後將輸出一模一樣的紅色。

如果我們將第一個數值改成0.2的話,輸出的紅色將會因此變小:

我不認為那是用直接乘的,因為輸出的顏色色碼上顯然沒有相等於乘出來的結果,顯然還有更深入的乘法,但這裡不探討。

使用注意:values=”…”中,引號要直接接數值不要有空格,也不要有換行,否則將導致Safari無法讀取濾鏡…很奇怪吧,目前只有看到這篇討論,有興趣可以去看看

<feColorMatrix type="matrix" values="
1 0 0 0 0 //錯誤,引號後面直接加上數值才行
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0" />

<feImage>

嵌入圖檔,嵌入的圖當將會顯示成點陣圖,即使嵌入SVG也一樣。

這個方法可以將圖檔嵌入外部圖檔。設計師指需要一個屬性:xlink:href (連結路徑),用href代替xlink:href也可以,只是如此只能支援SVG 2以下版本,使用SVG 2以下的Safari將不能支援。如果設計師是在React中使用xlinkHref可用代替xlink:href。

<feImage xlink:href="https://developer.mozilla.org/files/6457/mdn_logo_only_color.png" />

<feGaussianBlur>

製作高斯模糊,主要需要屬性in、stdDeviation即可。

<feGaussianBlur in="SourceGraphic" stdDeviation="5" />

<pattern>

pattern並不屬於filter之一,但經常與其他濾鏡配合,將它放至於<defs>之中即可使用:

<defs>
<pattern id="star" viewBox="0,0,10,10" width="10%" height="10%">
<polygon points="0,0 2,5 0,10 5,8 10,10 8,5 10,0 5,2"/>
</pattern>
</defs>
<rect width="100" height="100" fill="url(#star)"/>

pattern常用在重複的四方連續。設計師可以在<pattern>內製作想要重複的圖像,被套用的物件將被當做畫布來顯示四方連續。與filter不同的地方是,Pattern使用fill而不是filter屬性來套用。

事實上線條也可以被當做畫布製作,線條有寬度即可。

Pattern與filter中的feTile相當類似,但本文認為pattern使用方式較feTile簡單,可以直接替代。

<feComposite>

feComposite用在兩個形狀的布林運算

3D軟體如Ds Max也有相似的功能:A 減 B(A物件減去B物件的體積)、B減 A(B物件減去A物件的體積)、Union(A物件與B物件合體)與Intersection(A物件與B重疊的地方)

數學上,也有交集、聯集、互斥聯集與差集。程式語言上也有邏輯運算或||、與&&,都是相似的概念。甚至設計師的繪圖軟體如Illustrator也有同樣概念的路徑管理員。

feComposite有三個主要的屬性,in, in2, operator。很容易理解就是兩個輸入圖像,與一個運算符。運算符有over, in, out, xor, atop, arithmetic。

<feFlood flood-color="#0000ff" x="0" width="100" height="100" result="img2" /><feFlood flood-color="#ff0000" x="50" width="100" height="100" result="img1"/><feComposite operator="over" in="img1" in2="img2"/>
in的img1蓋在img2上面

上面的範例的運算模式不會做任何事情,但如果運算符是in,代表第一個輸入圖像的形狀將在第二的圖像元素範圍內,是第二個輸入圖像裁減第一個輸入圖像的範圍之意。

img1的形狀被img2剪裁

所有運算模式over, in , out, xor, atop大概是這個樣子:

至於arithmetic較為特殊,如果有興趣可以參考官方文件,是一個關於透明度的運算模式。

實務上的使用

濾鏡時往往是不只用一個,在用多個的時候必須考慮到輸入圖像in, in2的設定。在使用多個濾鏡時會需要考慮到客戶端電腦的效能,但只要圖片不要太大、套用濾鏡的形狀沒有太複雜,基本上不會有什麼問題。

好看的濾鏡使用像是 David Khourshid 的作品Alex the CSS Husky,是使用<feTurbulence>與<feDisplacement>濾鏡製作出來的,非常有趣。

相容性的問題需要被考慮,當設計師在製作SVG時,不時可以用Safari打開來檢視,或是製作一個實驗性的svg圖檔並用Safari開啟。Safari在SVG支援方面較其他瀏覽器不足。

希望能夠在SVG濾鏡使用上幫助大家。

--

--