原文地址:https://css-tricks.com/state-css-reflections 譯者:nzbin 友情提示:由於演示demo的相容性,推薦火狐瀏覽。該文章篇幅較長,內容龐雜,有一定難度。而我本人學識有限,加之時間倉促,所翻譯內容可能有不恰當及晦澀之處。歡迎大家拍磚指正。 我最近在 ...
原文地址:https://css-tricks.com/state-css-reflections
譯者:nzbin
友情提示:由於演示demo的相容性,推薦火狐瀏覽。該文章篇幅較長,內容龐雜,有一定難度。而我本人學識有限,加之時間倉促,所翻譯內容可能有不恰當及晦澀之處。歡迎大家拍磚指正。
我最近在codePen上看到了這個 載入程式,一個純CSS製作的帶有漸變反射的3D旋轉豎條。它的製作方法是:為每個豎條創建一個元素,然後通過複製每一個元素來製作反射倒影,最後在反射倒影上添加漸變背景來製作漸隱的效果。聽上去有點複雜,而且創建漸隱效果的漸變背景在非純色背景下是無效的。有沒有更好的CSS方法呢?
答案是‘肯定’的,也是‘否定’的。‘肯定’是因為確實有可以做的方法,‘否定’是因為有些方法還不存在。令人遺憾的是,這些代碼只能用預處理器(主要通過迴圈來實現壓縮功能)壓縮一點點。如果我們不想使用canvas並且想相容主流瀏覽器的當前版本,複製豎條來製作倒影以及通過漸變背景來製作漸隱效果的方法仍然是最好的。
這篇文章主要探索現有的製作反射倒影的方法、舉例說明可能的解決方案、跨瀏覽器問題以及我的一些想法。
Demo的基本設置
#創建豎條元素
首先我們創建一個 .loader 元素以及在其中創建10個 .bar 元素
HTML<div class='loader'> <div class='bar'></div> <!-- repeat to create 9 more bars --> </div>
把同樣的事情寫很多遍是一件痛苦的事,所以在該情況下使用一個預處理器會變得很簡單。我們在這裡使用 Haml 模板,當然也有人會選擇其他的模板。
Haml.loader
- 10.times do
.bar
我們通過絕對定位把所有元素放到視圖的中間。大多數情況下,我們使用 top: 50% ,但是現在,使用 bottom: 50% 會使後面的操作更簡單。
CSSdiv { position: absolute; bottom: 50%; left: 50%; }
我們給豎條設置 width 和 height ,為了能看到它再設置一個 background
SCSS$bar-w: 1.25em; $bar-h: 5 * $bar-w; .bar { width: $bar-w;
height: $bar-h; background: currentColor; }
我們希望豎條的底部貼合視圖的水平中心線。設置 bottom: 50% 已經可以了。
現在所有的豎條都重合在一起,它們的左邊貼在垂直中心線上,底部貼在水平中心線上。
See the Pen bar loader 1.1 creating the bars by Zongbin Niu (@nzbin) on CodePen.
#定位豎條
我們需要讓最左邊的豎條和最右邊的豎條到垂直中心線的距離相等。這個距離就是豎條數量( $n )的一半乘以豎條的寬度( $bar-w )。原始demo用的是普通的CSS,我們會使用Sass來減少代碼量。
See the Pen initial (stacked) vs. final (distributed) by Zongbin Niu (@nzbin) on CodePen.
這意味著,從豎條的起始位置開始,我們需要將第一個豎條向左移動 .5 * $n * $bar-w 。左側是x軸的負方向,需要在前面加負號。所以第一個豎條的 margin-left 就是 -.5 * $n * $bar-w 。
第二個豎條(以0為基數,索引值是1)就是向右(x軸的正方向)移動 1 個豎條的寬度。所以第二個豎條的 margin-left 就是 -.5 * $n * $bar-w + $bar-w 。
第三個豎條(以0為基數,索引值是2)就是向右(x軸的正方向)移動 2 個豎條的寬度。所以第三個豎條的 margin-left 就是 -.5 * $n * $bar-w + 2 * $bar-w 。
最後一個(以0為基數,索引值是 $n - 1
)就是向右(x軸的正方向)移動 $n - 1 個豎條的寬度。所以最後一個豎條 margin-left 就是 -.5 * $n * $bar-w + ($n - 1) * $bar-w 。
See the Pen bar distribution by Zongbin Niu (@nzbin) on CodePen.
通常情況下,如果我們認為當前豎條的索引值是 $i (以0為基數),那麼 $i 豎條的 margin-left 就是 -.5 * $n * $bar-w + $i * $bar-w ,可以簡化為 ($i - .5 * $n) * $bar-w 。
這就允許我們使用Sass的迴圈來定位豎條。
SCSS$n: 10; @for $i from 0 to $n { .bar:nth-child(#{$i + 1}) { margin-left: ($i - .5 * $n) * $bar-w; } }
為了看清楚豎條的邊界,我們給它一個 box-shadow 。
See the Pen bar loader 1.2 positioning the bars by Zongbin Niu (@nzbin) on CodePen.
#給豎條添加漸變
豎條的背景色是從最左邊的深藍色( #1e3f57 )過渡到最右邊的淺藍色( #63a6c1 )。這聽上去很像 the Sass mix()
function 所做的。第一個參數是淺藍色,第二個參數是深藍色,第三個參數(稱作相對權重)表示將多少(百分比表示)淺藍色混合進去。
對於第一個豎條,這個數量就是 0% ( 0% 數量的淺藍色),混合結果就是深藍色。
對於最後一個豎條,這個數量是 100% ( 100% 數量的淺藍色,也就是 0% 數量的深藍色),得到的背景色就是淺藍色。
對於剩下的豎條,我們需要平均分佈的中間值。如果我們有 $n 個豎條,第一個豎條在 0% 的位置,最後一個豎條在 100% 的位置,那麼我們需要在兩者之間平分成 $n-1 個區間。
See the Pen distribute n points on 100% interval (interactive) by Zongbin Niu (@nzbin) on CodePen.
一般來說,索引值為 $i 的豎條的相對權重是 $i * 100% / ($n - 1) ,這意味著我們要添加如下代碼:
SCSS$c: #63a6c1 #1e3f57; // 1st = light 2nd = dark @for $i from 0 to $n { // list of mix() arguments for current bar $args: append($c, $i * 100% / ($n - 1)); .bar:nth-child(#{$i + 1}) { background: mix($args...); } }
現在這些豎條看起來就和原始demo的一樣了:
See the Pen bar loader #1.3 shading the bars by Zongbin Niu (@nzbin) on CodePen.
探索反射的方案
#WebKit瀏覽器:-webkit-box-reflect
很遺憾,這不是一個標準屬性!我不知道為什麼這個屬性沒有標準化。這一屬性首次出現在Safari瀏覽器上時,我還不知道CSS。 但是對於WebKit內核的瀏覽器,這是一個非常好的實現方法。它做了很多工作。它的使用很簡單,即使在不支持該屬性的瀏覽器上,除了不顯示反射以外,並沒有什麼其他影響。
讓我們看看它是怎麼工作的,它需要三個參數值:
- 方向:包含 below , left , above , right
- 偏移值(可選):指定反射的開始位置到元素的底邊的距離(這是一個CSS長度值)。
- 圖片遮罩(可選):可以是CSS漸變值。
See the Pen how `-webkit-box-reflect` works by Ana Tudor (@thebabydino) on CodePen.
註意:linear-gradient()
可以有更多的顏色斷點,也可以用radial-gradient()
替換。
在我們的demo中,我首先想到的就是給 .loader 元素添加這一屬性。
SCSS.loader { -webkit-box-reflect: below 0 linear-gradient(rgba(#fff), rgba(#fff, .7)); }
但是在WebKit瀏覽器中測試時,我們並沒有看到反射。
See the Pen bar loader 2.1.1 -webkit-box-reflect by Zongbin Niu (@nzbin) on CodePen.
這裡發生了什麼?我們給所有的元素設置了絕對定位,但是並沒有設置 .loader 元素的尺寸。所以這是一個寬高都為0的元素。
讓我們給 .loader 元素一個明確的尺寸,高度 height 等於豎條的高度 $bar-h ,寬度等於所有豎條的寬度之和 $n * $bar-w 。為了看清元素的邊界,我們暫時給它一個 box-shadow 。
SCSS$loader-w: $n * $bar-w; .loader { width: $loader-w; height: $bar-h; box-shadow: 0 0 0 1px red; }
我之所以用 box-shadow 而不用 outline 是因為如果子元素溢出父元素,在不同的瀏覽器上使用 outline
突出物體的效果是不一樣的。
outline屬性在WebKit瀏覽器中的對比。Edge(上)vs. Firefox(下)
添加以上代碼後的結果可以在WebKit瀏覽器中看到:
See the Pen bar loader 2.1.2 explicitly sizing the loader by Zongbin Niu (@nzbin) on CodePen.
如果你用的不是WebKit瀏覽器,看下麵的圖片,就是這個樣子:
現在我們可以看到loader元素的邊界和倒影,但是位置不正確。我們希望loader元素在水平中間的位置,所以把它向左移動寬度的一半。我們也希望子元素的底部與父元素的底部貼合,所以設置子元素 bottom: 0 。
SCSS.loader { margin-left: -.5 * $loader-w; } .bar { bottom: 0; }
修正位置之後的樣子如下:
See the Pen bar loader 2.1.3 tweaking loader and bar positions by Zongbin Niu (@nzbin) on CodePen.
#火狐瀏覽器 element() + mask
##用 element() 製作反射
element() 函數(現在仍然有效,必須在火狐瀏覽器中使用並且添加 -moz- 首碼)給我們提供了一個像真實圖片一樣可以任意使用的圖像值。它需要一個參數值,就是我們希望以 background 還是 border-image 顯示的被選元素的 id 。這允許我們做很多事情,比如使用可以控制的圖片作為背景。我們也可以在Firefox中製作一個反射元素。
需要著重瞭解的一點就是 element() 函數不是遞歸函數,我們不能創建使用元素作為自身背景的圖像。這在創建反射的loader元素的偽類上使用是安全的,因此我們不用創建額外的元素。
好吧,讓我們看看如何操作。首先,我們給 .loader 元素一個 id 。轉到樣式表,我們從適用WebKit瀏覽器的CSS著手。我們在loader元素上添加一個 ::after 偽類
CSS.loader::after { position: absolute; top: 0; right: 0; bottom: 0; left: 0; box-shadow: 0 0 0 2px currentColor; color: crimson; content: 'REFLECTION'; }
為了在最終效果中看清偽類的邊界和方向,我們設置了一些暫時性的樣式,我們想讓它是顛倒的。
See the Pen bar loader 2.2.1 adding a pseudo by Zongbin Niu (@nzbin) on CodePen.
現在我們需要以底邊為基準把 ::after 偽類鏡像過來。為了做到這一點,我們使用 scaleY() 屬性並且選擇合適的 transform-origin 。
以下的可交互demo說明瞭包含多個縮放因數以及變換中心的定向縮放是如何工作的:
See the Pen how CSS scaling w.r.t. various origins works by Ana Tudor (@thebabydino) on CodePen.
註意:縮放因數的數值和變換中心可以超出demo中規定的限制。
在我們的例子中,我們需要 scaleY(-1) 並且 transform-origin 在 ::after 偽類的底邊上。
使用scaleY(-1)
和一個合適的 transform-origin
來鏡像元素
我們把這些設置添加到代碼中,並且用 element () 函數把 ::after 偽類的背景設置為 #loader
CSS.loader::after { transform-origin: 0 100%; transform: scaleY(-1); background: -moz-element(#loader); }
註意:由於特別的原因我們使用 .loader 作為選擇器並且由於element() 函數參數的需要我們設置它的 id 為 #loader 。
添加以上代碼後的效果如下所示(只在Firefox瀏覽器中有效)
See the Pen bar loader 2.2.2 -moz-element() for reflecting pseudo by Zongbin Niu (@nzbin) on CodePen.
對於在其他瀏覽器閱讀這篇文章的朋友,以下是截圖
在Firefox中使用 element() 製作的反射效果
##用 mask 製作漸變
我們使用和WebKit情況下一樣的方法給反射添加漸變。在WebKit的情況下,遮罩是 -webkit-box-reflect 屬性的一部分。而現在,我們討論CSS的 mask 屬性,它需要引用SVG作為值。
CSSmask: url(#fader);
#fader 元素是一個包含長方形的SVG mask 元素。
SVG<svg> <mask id='fader' maskContentUnits='objectBoundingBox'> <rect width='1' height='1'/> </mask> </svg>
我們可以用Haml模板壓縮一下
Haml%svg
%mask#fader(maskContentUnits='objectBoundingBox')
%rect(width='1' height='1')
但是,如果我們加上以上代碼,我們的反射倒影消失了,在Firefox中查看如下demo
See the Pen bar loader 2.2.3 adding a SVG mask by Zongbin Niu (@nzbin) on CodePen.
這是因為,預設情況下,SVG圖形會有一個純黑色的 fill ,完全不透明,但是,我們的 遮罩 預設是有透明度的。因此為了實現反射漸變的效果我們需要給長方形一個 fill (需引入SVG linearGradient )
Haml%rect(width='1' height='1' fill='url(#grad)')
一個SVG linearGradient 由定義的兩個點 x1 , y1 , x2 , y2 組成。 x1 和 y1 是漸變線的起始點(0%)坐標,而 x2 和 y2 是這條線的終點(100%)坐標。如果這些數值是空的,預設設為 0% , 0% , 100% , 0% 。這些數值描繪了從指定元素(由於 gradientUnits 的預設值是 objectBoundingBox )的左上( 0% 0% )到右上( 100% 0% )的一條線。這意味著,預設情況下,漸變是從左到右。
但是在我們的例子中,我們希望漸變從上到下,所以我們將 x2 的值從 100% 設置為 0% 並且將 y2 的值從 0% 設置為 100% 。這使得指定元素的漸變向量從左上角( 0% 0% )指向左下角( 0% 100% )。
Haml%linearGradient#grad(x2='0%' y2='100%')
在 linearGradient 元素之內,我們至少需要兩個 stop 元素。其中有三個特定的屬性, 偏移值 , 顏色斷點 , 透明度斷點。
- 偏移值 :可以使用百分比,通常在 0% 到 100% 之間,和CSS漸變一樣。也可以使用數值,通常是從0到1。
- 顏色斷點 :理論上可以使用關鍵字 hex() , rgb() , rgba() , hsl() 或者 hsla() ,實際上Safari不支持半透明數值,因此如果想設置漸變為半透明,我們需要依賴第三個屬性。
- 透明度斷點 :可以設置從0(完全透明)到1(完全不透明)的數值。
我們需要記住我們應用漸變遮罩的偽類已經通過 scaleY(-1) 屬性鏡像過來了,這意味著漸變遮罩的底部在視覺上是頂端。因此我們的漸變是從頂部(視覺下端)的完全透明到底部(視覺上端)的 .7 。
因為我們的漸變從上到下,所以第一個 stop 是完全透明的。
Haml%linearGradient#grad(x2='0%' y2='100%')
%stop(offset='0' stop-color='#fff' stop-opacity='0')
%stop(offset='1' stop-color='#fff' stop-opacity='.7')
添加線性漸變之後,在Firefox中就是我們想要的結果了
實時效果如下:
See the Pen bar loader 2.2.4 linearGradient it by Zongbin Niu (@nzbin) on CodePen.
SVG漸變的問題
在我們的例子中,因為我們的遮罩漸變是垂直的所以看起來很簡單。但是如果我們的漸變不是垂直或者水平或者從一個角到另一個角怎麼辦?如果我們想要一個特定角度的漸變怎麼辦?
SVG中有一個 gradientTransform 屬性,它可以通過指定 x1 , y1 , x2 , y2 來旋轉漸變線。有人可能會認為這是製作具有特定角度的CSS漸變的簡單方法。但是,並沒有想象的那麼簡單!
想一想從金色到深紅色漸變的例子。為了看得清楚一點,我們在兩者之間50%的位置設置一個劇烈的過渡。首先我們將這個漸變的CSS角度設置為 0deg 。這意味著漸變會從底部(金色)過渡到頂部(深紅色)。創建這個漸變的CSS如下:
CSSbackground-image: linear-gradient(0deg, #e18728 50%, #d14730 0);
如果你還不明白CSS線性漸變的工作原理,你可以看一下Patrick Brosset 做的這個 優秀作品。
我們看到的結果如下:
See the Pen CSS linear-gradient at 0deg with sharp stop at 50% by Ana Tudor (@thebabydino) on CodePen.
為了製作SVG的漸變,我們設置 y1 為 100% y2 為 0% 以及把 x1 和 x2 設置成相同的數值(簡單起見設置為0)。這意味著漸變線從底部垂直上升到頂部。我們同時把斷點的偏移值設置為50%。
JadelinearGradient#g(y1='100%' x2='0%' y2='0%' gradientTransform='rotate(0 .5 .5)')
stop(offset='50%' stop-color='#e18728')
stop(offset='50%' stop-color='#d14730')
編輯註:我問Ana為什麼她現在要使用Jade模板。她說:我起初使用Haml模板是因為我想避免引入我不需要的迴圈變數,而之後使用Jade模板是因為它允許變數和計算。
這個漸變還沒有旋轉,因為 gradientTransform 的值是 rotate(0 .5 .5) 。其中後兩個數值表示漸變旋轉的坐標。其中 0 0 表示左上角, 1 1 表示右下角, .5 .5 表示中心。
實時效果如下:
See the Pen SVG linearGradient bottom to top rotated by 0deg with sharp stop at 50% by Ana Tudor (@thebabydino) on CodePen.
如果我們希望漸變從左到右,在CSS漸變中,我們把角度從 0deg 設置為 90deg
CSSbackground-image: linear-gradient(90deg, #e18728 50%, #d14730 0);
See the Pen CSS linear-gradient at 90deg with sharp stop at 50% by Ana Tudor (@thebabydino) on CodePen.
為了在SVG漸變中得到同樣的結果,我們將 gradientTransform 的值設置為 rotate(90 .5 .5) 。
JadelinearGradient#g(y1='100%' x2='0%' y2='0%' gradientTransform='rotate(90 .5 .5)')
// same stops as before
See the Pen SVG linearGradient bottom to top rotated by 90deg with sharp stop at 50% by Ana Tudor (@thebabydino) on CodePen.
到目前為止,一切正常。用SVG來代替CSS漸變並沒有遇到太多問題。讓我們嘗試一下其他的角度。在下麵的可交互demo中,左側是一個CSS漸變,右邊是一個SVG漸變。紫色的線是漸變線,它與金色和深紅色的交界線是垂直的。拖拽滑塊可以同時改變CSS和SVG的漸變角度。我們會看到一些錯誤:有些數值不是 90deg 的倍數。
See the Pen CSS vs. SVG gradient, same angle (interactive, responsive) by Ana Tudor (@thebabydino) on CodePen.
如以上demo所示,有些數值不是 90deg 的倍數,我們無法得到相同的結果。只有當我們設置漸變的元素是正方形時結果是相同的。這意味著我們可以給一個更大的正方形元素設置漸變,然後裁剪成實際的形狀。然而做這些工作會讓 element() 和 mask 來創建漸變倒影的方法更加複雜。
#Edge:可以全用SVG嗎?
令人遺憾的是,以上提到的兩種方法在Edge中都沒有用。因此既能在Edge中運行又無需手動複製每個豎條的僅有的方法就是,放下前面的工作重新製作SVG載入器。這中方法具有跨瀏覽器的優勢。
總的來說,我們創建一個帶有 viewBox 的SVG元素,以便把 0 0 點放在中間。我們定義一個豎條,它的底邊在x軸上,左邊在y軸上。然後我們在 #loader 群組中根據需要複製(通過SVG use 元素)多次。我們如之前一樣放置這些豎條的位置。
Jade- var bar_w = 125, bar_h = 5 * bar_w; - var n = 10; - var vb_w = n * bar_w; - var vb_h = 2 * bar_h; - var vb_x = -.5 * vb_w, vb_y = -.5 * vb_h; svg(viewBox=[vb_x, vb_y, vb_w, vb_h].join(' ')) defs rect#bar(y=-bar_h width=bar_w height=bar_h) g#loader - for(var i = 0; i < n; i++) { - var x = (i - .5 * n) * bar_w; use(xlink:href='#bar' x=x) - }
以上代碼的效果如下:
See the Pen bar loader 2.3.1 creating and positioning SVG bars by Zongbin Niu (@nzbin) on CodePen.
現在我們已經創建了所有豎條,我們想把svg元素的位置調整的更準確而且我們使用flexbox屬性。同時我們也和之前一樣給豎條添加漸變色。我們用Sass做這些事情
SCSS$n: 10; $c: #63a6c1 #1e3f57; $bar-w: 1.25em; $bar-h: 5 * $bar-w; $loader-w: $n * $bar-w; $loader-h: 2 * $bar-h; body { display: flex; justify-content: center; margin: 0; height: 100vh; } svg { align-self: center; width: $loader-w; height: $loader-h; } @for $i from 0 to $n { $args: append($c, $i * 100%/($n - 1)); [id='loader'] use:nth-child(#{$i + 1}) { fill: mix($args...); } }
添加以上代碼後的效果如下:
See the Pen bar loader 2.3.2 sizing + positioning the SVG & shading the bars by Zongbin Niu (@nzbin) on CodePen.
我們複製 #loader 群組(再次使用 use 元素)。我們通過 scale(1 -1) 方法鏡像克隆體並且給它添加一個遮罩,和我們之前給偽類元素設置的一樣。預設情況下,SVG元素相對於SVG畫布的 0 0 點縮放,這個點正好位於loader元素的底邊上,可以很完美的將loader元素鏡像過來,我們不用設置 transform-origin 。
Jade/SVGuse(xlink:href='#loader' transform='scale(1 -1)')
我們使用 transform 屬性代替CSS變換,因為CSS變換在Edge中不支持。
現在我們有了反射倒影,如下所示:
See the Pen bar loader 2.3.3 getting the reflection by Zongbin Niu (@nzbin) on CodePen.
最後一步就是用 mask 給反射添加漸變。它的方法及代碼和之前都是一樣的,我們不再贅述。所有的代碼都在下麵的Pen中
See the Pen bar loader 2.3.4 fading the reflection by Zongbin Niu (@nzbin) on CodePen.
動畫
原始案例中的CSS動畫很簡單,就是用3D方式旋轉豎條:
CSS@keyframes bar { 0% { transform: rotate(-.5turn) rotateX(-1turn); } 75%, 100% { transform: none; } }
所有的豎條都是同樣的動畫:
CSSanimation: bar 3s cubic-bezier(.81, .04, .4, .7) infinite;
我們只是給迴圈的豎條添加了不同的延遲時間。
SCSSanimation-delay: $i*50ms;
因為我們希望旋轉的豎條具有3D效果,所有我們給loader元素添加一個 perspective 屬性
但是使用 -webkit-box-reflect 方法後和預期的一樣只能在WebKit瀏覽器中執行。
在Chrome瀏覽器中使用 -webkit-box-reflect 屬性後的最終結果
我們同時添加了一張背景圖片來看一下它的表現效果。只能在WebKit瀏覽器中預覽的效果如下:
See the Pen bar loader 3.1 animating the bars by Zongbin Niu (@nzbin) on CodePen.
我們也嘗試在Firefox中執行動畫。但是,如果我們把動畫添加到之前在Firefox中運行良好的代碼中,好像出現了一些問題。
在Firefox中使用element()和mask方法做的動畫雛形
這裡有一點問題,下麵的demo可以在Firefox中實時檢測:
See the Pen bar loader 3.2.1 adding animation by Zongbin Niu (@nzbin) on CodePen.
第一個問題就是反射在偽類的邊界處被切斷。我們可以通過增加loader元素的尺寸來修複這一問題(偽類元素不受影響):
SCSS$loader-w: ($n + 1) * $bar-w + $bar-h;
但是我們對於其餘的兩個問題就束手無策了。當豎條進行3D旋轉時,反射無法平滑的渲染更新;以及 perspective 屬性導致了豎條的消失。
添加perspective屬性的結果 沒有添加perspective屬性的結果
以下是實時的顯示結果:
See the Pen bar loader 3.2.2 tweaks by Zongbin Niu (@nzbin) on CodePen.
全部都用SVG的方案怎麼樣?很不幸,上面的例子中,我們只用CSS的3D變化製作動畫。在Edge中,SVG元素不支持CSS的變換屬性,所以我們之前在創建倒影時使用了SVG的 transform 屬性。但是 transform 屬性是嚴格的2D模式,我們只能使用JavaScript添加動畫。
所以就目前來看,想要製作一個相容所有瀏覽器並且不用複製每一個豎條的載入動畫是不可能了。我們現在能做的就是創建兩個loader元素,每一個都有相同數量的豎條。
Haml- 2.times do
.loader
- 10.times do
.bar
豎條的樣式和之前一樣,我們使用 scale(-1) 來鏡像第二個loader元素。
CSS.loader:nth-child(2) { transform: scaleY(-1); }
我們添加豎條動畫後得到如下結果:
See the Pen bar loader 3.3.1 reflection via duplication by Zongbin Niu (@nzbin) on CodePen.
現在我們需要給反射添加漸變。遺憾的是,我們不能在第二個loader元素上使用 mask ,因為它只在跨瀏覽器的SVG元素上有效。Edge目前還不支持HTML元素的遮罩效果,但是你可以給官方提建議。
我們只能在第二個loader元素上添加漸變背景。這樣一來我們就不能使用圖片背景了。漸變背景只在純色背景或者有限的情況下才有效。我們在第二個loader元素的 ::after 上添加漸變背景並且設置的大一點,這樣就不會擋住旋轉的豎條。
SCSS$bgc: #eee; $cover-h: $bar-w + $bar-h; $cover-w: $n * $bar-w + $cover-h; html { background: $bgc; } .loader:nth-child(2)::after { margin-left: -.5 * $cover-w; width: $cover-w; height: $cover-h; background: linear-gradient($bgc $bar-w, rgba($bgc, .3)); content: ''; }
最終結果如下:
See the Pen bar loader 3.3.2 emulate fading with cover by Zongbin Niu (@nzbin) on CodePen.
最後的思考
我們需要一個更好的跨瀏覽器解決方案。我相信製作物體的反射並不需要像我們在這個例子中一樣複製所有的子元素。為了製作可以放置在背景圖像上的漸變反射,我們不能替換成SVG的方案(其自身也有很多問題)。
哪一種方案更好? -webkit-box-reflect 還是 element() + mask ?我也不知道。我個人喜歡同時使用。
雖然使用 :reflection
偽類元素 看上去很合理,但是我曾經確信我不想使用額外的元素製作反射。但是現在有比不用插入額外元素更讓我喜歡的事情。使用 element() 可以在不同方向上自由創建多個反射,以及用不同的方式變換反射,比如3D旋轉或者傾斜。這正是我喜歡它的原因。而且用SVG做遮罩意味著我們可以在反射上應用更複雜的遮罩同時獲得更酷的效果。
另一方面,能力越強,責任越大。也許你沒有時間去接觸強大功能背後的複雜細節。有時你只是想要一個簡單的方法來獲得一個簡單的結果。