關於CSS反射倒影的研究思考

来源:http://www.cnblogs.com/nzbin/archive/2016/09/24/5895335.html
-Advertisement-
Play Games

原文地址: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% 會使後面的操作更簡單。

CSS
div {
  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 , leftabove , 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作為值。

CSS
mask: 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 元素。其中有三個特定的屬性, 偏移值 , 顏色斷點 , 透明度斷點。

  1.  偏移值 :可以使用百分比,通常在 0% 100% 之間,和CSS漸變一樣。也可以使用數值,通常是從0到1。
  2.  顏色斷點 :理論上可以使用關鍵字 hex()rgb() , rgba() , hsl() 或者 hsla() ,實際上Safari不支持半透明數值,因此如果想設置漸變為半透明,我們需要依賴第三個屬性。
  3.  透明度斷點 :可以設置從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如下:

CSS
background-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%。

Jade
linearGradient#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 

CSS
background-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)

Jade
linearGradient#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/SVG
use(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; }
}

 所有的豎條都是同樣的動畫:

CSS
animation: bar 3s cubic-bezier(.81, .04, .4, .7) infinite;

 我們只是給迴圈的豎條添加了不同的延遲時間。

SCSS
animation-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做遮罩意味著我們可以在反射上應用更複雜的遮罩同時獲得更酷的效果。

另一方面,能力越強,責任越大。也許你沒有時間去接觸強大功能背後的複雜細節。有時你只是想要一個簡單的方法來獲得一個簡單的結果。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 今天使用webpack-sample初始一個vue-cli項目,在app.vue文件中添加了個鉤子函數ready,可是ready內的事件一直不執行,檢查了webpack文件和package.json也沒發現什麼問題,瀏覽器也沒報錯或者提示,很令人捉急。然後去github看了webpack-simpl ...
  • 在命名規則上,構造函數一般是首字母大寫,普通函數遵照小駝峰式命名法。 在函數調用的時候: function fn() { } 構造函數:1. new fn( ) 2 .構造函數內部會創建一個新的對象,即f的實例 3. 函數內部的this指向 新創建的f的實例 4. 預設的返回值是f的實例 普通函數: ...
  • 當解析器遇到 script 標簽時,文檔的解析將停止,並立即下載並執行腳本,腳本執行完畢後將繼續解析文檔。但是我們可以將腳本標記為 defer,這樣就不會停止文檔解析,等到文檔解析完成才執行腳本,也可以將腳本標記為 async,以便由其他線程對腳本進行解析和執行。 <! more 三者之間的區別? ...
  • 作為Web開發者,這是好的時代,也是壞的時代。Web開發技術也在不斷變化。雖然很令人興奮,但是這也意味著Web開發人員需要要積極主動的學習新技術和新的編程語言,並願意和渴望接受新的挑戰,以適應變化。新的挑戰可能會包括一些開發上的要求,如利用適應現有的框架來滿足業務需求。測試一個網站,能從中知道出了哪 ...
  • 今天遇到關於javascript原型的一道面試題,現分析下: 原題如下: function A(){ } function B(a){ this.a = a; } function C(a){ if(a){ this.a = a; } } A.prototype.a = 1; B.prototype ...
  • border-radius:允許向元素添加圓角 box-shadow:陰影 設置多層陰影 box-shadow:10px 10px 5px 5px gray,15px 15px 5px 5px blue,20px 20px 5px 5px gray;/* 多層陰影*/ border-image屬性用 ...
  • 一.冒泡排序 var arr1=[3,9,2,7,0,8,4]; for(var i=0;i<arr1.length;i++){ for(var j=i+1;j<arr1.length;j++){ var temp=0; if(arr1[i]>arr1[j]){ temp=arr1[i]; arr1 ...
  • 先上圖 1.一些常用的方法 obj.getElementById() 返回帶有指定 ID 的元素。 obj.getElementsByTagName() 返回包含帶有指定標簽名稱的所有元素的節點列表(集合/節點數組)。 obj.etElementsByClassName() 返回包含帶有指定類名的所 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...