天天看點

box-shadow/drop-shadow/feDropShadow 投影的前世今生

緣起

這是在做一個呼吸燈動效時遇到的問題,就是如何給SVG的圖形元素加上一些陰影的效果,比如外發光,比如内發光,比如投影,之前也有困擾,但是因為沒到非解決不可的程度,是以就暫時擱置了。對于發光這種效果,對于CSS3來說,很簡單的事情,一個box-shadow屬性可以解決所有問題,我曾經寫過一篇關于CSS3樣式的文章UI設計師進階技能——CSS3之樣式篇裡枚舉的非常全面,包括各個參數如何去和PS一一對應,不但能設定是外陰影還是内陰影,尺寸,X和Y的偏移,還可以多種效果去疊加,玩好了,當成是畫圖神器都是有可能的。

box-shadow的局限性

這是直接給一個100*100的div盒子定義了外發光後的效果,為了友善修改顔色,用了hsla的色值表示方法。

#box{
height:;
width:;
background: hsla(168,90%,45%,1);
border-radius: ;
box-shadow:   hsla(168,90%,45%,1);
}
複制代碼
           

一個完美的外發光效果,試一下将box-shadow屬性定義到動畫規則中,會發生什麼:

/*定義一個透明度變化(hsla的a值改變)動畫規則*/
@keyframes outLight{
	0%{box-shadow:   hsla(168,90%,45%,1)}
	100%{box-shadow:   hsla(168,90%,45%,0.1)}
}
#box {
	height:;
	width:;
	background: hsla(168,90%,45%,1);
	border-radius: ;
	animation: outLight  ease infinite;
}
複制代碼
           

可以輕松實作這種透明度變化産生的“砰砰”動效。那再試一下改顔色,為了少寫一個動畫規則,我先不給盒子填充任何背景顔色,僅保留box-shadow效果。之是以用hsla色值表示方法,也是為了調整顔色友善,隻要改一下h值就可以了。

/*定義一個顔色變化(hsla的h值改變)動畫規則*/
@keyframes outLight{
	0%{box-shadow:   hsla(168,90%,45%,1)}
	100%{box-shadow:   hsla(220,90%,45%,1)}
}
#box {
	height:;
	width:;
	border-radius: ;
	animation: outLight  linear alternate infinite;
}
複制代碼
           

變色也很OK,其他值的改變就不再做嘗試了。

這裡我試着做了一排依次按順序點亮的信号燈效果,任意一個時刻都是四個不同的顔色。

這裡,我如果想做一個圓環的外發光效果,抱歉,box-shadow屬性告訴你,這是天方夜譚,因為字面意思來講,box——盒子,作為盒子模型,支援的是一個整體的外觀,或者稱之為邊緣效果。

drop-shadow濾鏡的補充

drop-shadow

确切來說是濾鏡

filter

的一種,還有常見的模糊濾鏡blur,黑白濾鏡grayscale等等,這次先隻說這個投影。文法簡單:

.box {
	height:;
	width:;
	border-radius:;
    /*filter:drop-shadow 濾鏡*/
	filter: drop-shadow(0 0 30px hsla(168,90%,45%,1));
	background-color: hsla(168,90%,45%,1);
}
複制代碼
           

也可以得到一個完美的外發光效果,但這裡有個很大的坑,就是會受元素填充顔色的影響,比如,改掉背景顔色的透明度

background-color: hsla(168,90%,45%,0.2)

,會發現投影效果也會發生變化。

drop-shadow

濾鏡是不像

box-shadow

屬性那樣支援多個設定疊加的,因為畢竟

filter

才是屬性,是以投影的濾鏡隻能設定一個。但是,濾鏡因為是加給實體的,不受什麼盒子模型影響,是以,當我的盒子是一個描邊而非填充樣式時

filter: drop-shadow(0 0 8px hsla(168,90%,45%,1));
border:10px solid hsla(168,90%,45%,1);
複制代碼
           

就能得到一個内外發光的圓環了。

這個搭配SVG可謂無往不利,比如,想給下面這種複雜的SVG圖形加一個投影,直接讓SVG使用定義好的濾鏡就可以了。

本來就是一個普通平面的SVG圖形,加了

filter="drop-shadow(0 10px 5px hsla(168,0%,45%,0.4))"

的定義後,瞬間躍然紙上。而且投影的邊緣完全就是圖形的邊緣,這可是

box-shadow

屬性無能為力的事情。

來試一下對動畫的支援。

/*定義一個投影效果透明度變化(hsla的a值改變)動畫規則*/
@keyframes outLight{
0%{filter: drop-shadow(0 0 10px  hsla(168,90%,45%,1))}
100%{filter: drop-shadow(0 0 10px  hsla(168,90%,45%,0.1))}
}
複制代碼
           

依然可以得到動效。變色就不做了,也是支援的。

現在,新的需求又來了,我要給SVG中的圖形元素加投影,首先聲明,單個圖形元素是不支援使用drop-shadow濾鏡屬性的,已經踩過這個坑了,那還有救沒?有。

SVG量身定制的feDropShadow投影濾鏡

上面的投影濾鏡是CSS的filter屬性,隻能把SVG當成一個圖檔元素整體來處理,終極目标來着,SVG+CSS3的動畫對不對?這需要把SVG裡面的圖形元素單獨屬性指派。SVG強大的

<def>

元素可以讓這一切都變成現實。我們要做的就是,首先定義一個id為outLight濾鏡,文法如下:

<defs>
    <filter id="outLight">
    <!-- 定義了一個水準和垂直偏移距離為0,大小為10的外發光效果 -->
      <feDropShadow dx="0" dy="0" stdDeviation="10" flood-color="hsla(180,90%,40%,0.9)" />
    </filter>
  </defs>
複制代碼
           

這裡有一些特殊的文法,dx和dy通過字面意思容易了解,就是水準和垂直的偏移距離,steDeviation是個什麼屬性?來看一下MDN文檔中對steDeviation的官方解釋:

The stdDeviation attribute defines the standard deviation for the blur operation.

用句簡單的話來解釋,就是投影的尺寸。

顔色的定義,屬性名為flood-color,同樣,如果不hsla或者rgba這種自帶透明度定義的色值表示方法,需要調節透明度的話,則通過flood-opacity屬性來定義。

我使用了一個描邊的圓形通過

filter:url(#outLight)

語句來調用這個濾鏡,效果卻并不理想:

外發光效果的實作沒有問題,但很明顯的看出似乎濾鏡作用的區域被截取了。追本溯源來找原因,關于

<filter>

濾鏡的屬性值這樣寫道:

The position and dimensions of a filter may be specified using the following parameters: x, y, width, height. The default values are:
  • x: -10%
  • y: -10%
  • width: 120%
  • height: 120%

簡而言之,就是濾鏡預設作用範圍為使用元素溢出10%,在上面這個案例中,因為發光的範圍較大,顯然10%的溢出是不夠的,既然預設值不合理,那就重新定義一下

<filter id="outLight" x="-50%" y="-50%"  width="200%" height="200%">
複制代碼
           

這樣的話,我給到了200%的溢出範圍,這樣就解決了外發光半徑超出預設作用區域的問題。

得到完美的外發光圓環。

可以随随便便修改dx和dy的值,比如下面這種:

甚至讓投影完全脫離本體

引申:

filter

濾鏡還能做什麼

在深入了解濾鏡之前,我一直把SVG定位為矢量圖形,結合最多的也是平面風格的插畫,但當祭出濾鏡神器,突然發現,可以用SVG來讓純色填充的圖形變得有質感,比如增加紋理,比如增加浮雕效果。

下面這張圖檔這樣看真的是平淡無奇,這是我在AI中繪制的基礎圖形。

燃鵝,當我按照下面圖層拆分後依次加上對應的濾鏡效果後

)

下面,就是見證奇迹的時刻:

瞧這凹凸有緻浮雕效果,瞧這底部細膩的紋理,是不是很難想象,在SVG中不過是下面這兩句濾鏡定義來實作的:

<!-- 給底層增加噪點效果的濾鏡 -->
<filter  filterUnits="objectBoundingBox" id="noise">
	<feTurbulence  baseFrequency="0.5" id="c2" in="c1" numOctaves="10" result="c2" stitchTiles="noStitch" type="fractalNoise">
	</feTurbulence>
	<feComposite  in="c2" in2="SourceAlpha" operator="in"></feComposite>
</filter>
<!-- 模糊濾鏡 頂部高光和底部陰影都可以使用 -->
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" />
</filter>
複制代碼
           

放幾個最終的效果來show一下技巧:

旋轉效果

閃爍效果:

關于

<filter>

深入了解的話,應該有意思的地方還有很多,目前我能完全弄明白隻有投影和模糊,等完全搞清楚之後,會來一篇單獨介紹濾鏡的專題。因為投影可以完全follow本體的變化,比如随便來個最簡單的縮放:

另外關于利用濾鏡做動效的創意,還在冥思苦想中,這又是另外一篇了。

小結一下
  • 通過

    <defs> <filter><feDropShadow ……/> </filter> </defs>

    的文法來定義一個投影效果的濾鏡。
  • 預設

    <filter>

    的作用範圍為10%的溢出,當投影尺寸大時,需要修改x、y、width、height的值。

轉載于:https://juejin.im/post/5cc013a9e51d456e2e656d70

ui