关于 CSS 反射倒影的研究思考

原文:The State of CSS Reflectionscss

译者:nzbinhtml

友情提示:因为演示 demo 的兼容性,推荐火狐浏览。该文章篇幅较长,内容庞杂,有必定难度。而我本人学识有限,加之时间仓促,所翻译内容可能有不恰当及晦涩之处。欢迎你们拍砖指正。web

我最近在 codePen 上看到了这个 加载程序,一个纯 CSS 制做的带有渐变反射的 3D 旋转竖条。它的制做方法是:为每一个竖条建立一个元素,而后经过复制每个元素来制做反射倒影,最后在反射倒影上添加渐变背景来制做渐隐的效果。听上去有点复杂,并且建立渐隐效果的渐变背景在非纯色背景下是无效的。有没有更好的 CSS 方法呢?canvas

答案是‘确定’的,也是‘否认’的。‘确定’是由于确实有能够作的方法,‘否认’是由于有些方法还不存在。使人遗憾的是,这些代码只能用预处理器(主要经过循环来实现压缩功能)压缩一点点。若是咱们不想使用 canvas 而且想兼容主流浏览器的当前版本,复制竖条来制做倒影以及经过渐变背景来制做渐隐效果的方法仍然是最好的。浏览器

这篇文章主要探索现有的制做反射倒影的方法、举例说明可能的解决方案、跨浏览器问题以及个人一些想法。sass

基本设置

建立竖条元素

首先建立一个 .loader 元素以及在其中建立10个 .bar 元素安全

HTML
<div class='loader'>
  <div class='bar'></div>
  <!-- repeat to create 9 more bars -->
</div>

把一样的事情写不少遍是一件痛苦的事,因此在该状况下使用一个预处理器会变得很简单。咱们在这里使用 Haml 模板,固然也有人会选择其余的模板。app

Haml
.loader
  - 10.times do
  .bar

 经过绝对定位把全部元素放到视图的中间。大多数状况下,咱们会使用 top: 50% ,可是如今,使用 bottom: 50% 会使后面的操做更简单。svg

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 Ana Tudor (@thebabydino) on CodePen.

定位竖条

咱们须要让最左边的竖条和最右边的竖条到垂直中心线的距离相等。这个距离就是竖条数量( $n )的一半乘以竖条的 width( $bar-w )。原始 demo 用的是普通的 CSS,咱们会使用 Sass 来减小代码量。

See the Pen initial (stacked) vs. final (distributed) by Ana Tudor (@thebabydino) on CodePen.

这意味着,从竖条的起始位置开始,咱们须要将第一个竖条向左移动 5 * $n * $bar-w 。左侧是 x 轴的负方向,须要在前面加 - 号。因此第一个竖条的 margin-left 就是 -.5 * $n * $bar-w

第二个竖条(以 0 为基数,索引值是 1)就是向右(x 轴的正方向)移动 1 个竖条的宽度($bar-w)。因此第二个竖条的 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 Ana Tudor (@thebabydino) 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 Ana Tudor (@thebabydino) 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 Ana Tudor (@thebabydino) 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 Ana Tudor (@thebabydino) on CodePen.

探索反射的方案

WebKit浏览器:-webkit-box-reflect

很遗憾,这不是一个标准属性!我不知道为何这个属性没有标准化。这一属性首次出如今Safari浏览器上时,我还不知道 CSS。 可是对于 WebKit 内核的浏览器,这是一个很是好的实现方法。它作了不少工做。它的使用很简单,即便在不支持该属性的浏览器上,除了不显示反射之外,并无什么其余影响。

让咱们看看它是怎么工做的,它须要三个参数值:

  • 方向:包含 belowleft , above , right
  • offset 偏移值(可选):指定反射的开始位置到元素的底边的距离(这是一个 CSS 长度值)。
  • 图片遮罩 mask(可选):能够是 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 Ana Tudor (@thebabydino) on CodePen.

这里发生了什么?咱们给全部的元素设置了绝对定位,可是并无设置 .loader 元素的尺寸。因此这是一个宽高都为 0 的元素。

让咱们给这个元素一个明确的尺寸,高度 height 等于竖条的高度 $bar-h ,宽度等于全部竖条的 width 之和 $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 而不用 box-shadow 是由于若是子元素溢出父元素,在不一样的浏览器上使用 outline 突出物体的效果是不同的。

outline属性在WebKit浏览器中的对比。Edge(上)vs. Firefox(下)

 

添加以上代码后的结果能够在 WebKit 浏览器中看到以下效果:

See the Pen bar loader 2.1.2 explicitly sizing the loader by Ana Tudor (@thebabydino) on CodePen.

若是你用的不是 WebKit 浏览器,看下面的图片,就是这个样子:

如今咱们能够看到 loader 元素的边界和倒影,可是位置不正确。咱们但愿 loader 元素在水平中间的位置,因此把它向左移动 width 的一半。咱们也但愿子元素的底部与父元素的底部贴合,因此设置子元素 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 Ana Tudor (@thebabydino) 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 Ana Tudor (@thebabydino) 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 中规定的限制。

在演示 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);
}

注意:因为特别的缘由咱们使用 .loaderr 做为选择器而且因为 element() 函数参数的须要咱们设置它的 id 为 #loader

添加以上代码后的效果以下所示(只在 Firefox 浏览器中有效)

See the Pen bar loader 2.2.2 -moz-element() for reflecting pseudo by Ana Tudor (@thebabydino) 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 Ana Tudor (@thebabydino) 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% )的一条线。这意味着,默认状况下,渐变是从左到右。

可是在咱们的例子中,咱们但愿渐变从 top 到 bottom ,因此咱们将 x2 的值从 100% 设置为 0% 而且将 y2 的值从 0% 设置为 100% 。这使得指定元素的渐变向量从左上角( 0% 0% )指向左下角( 0% 100% )。

Haml
%linearGradient#grad(x2='0%' y2='100%')

在 linearGradient 元素以内,咱们至少须要两个 stop 元素。其中有三个特定的属性, 偏移值 offset , 颜色断点 stop-color, 透明度断点 stop-color

  1.  偏移值 offset:可使用百分比 %,一般在 0% 到 100% 之间,和 CSS 渐变同样。也可使用数值,一般是从 0 到 1 。
  2.  颜色断点 stop-color:理论上可使用关键字 hexrgb() , rgba() , hsl() 或者 hsla() ,实际上 Safari 不支持半透明数值 ,所以若是想设置渐变为半透明,咱们须要依赖第三个属性。
  3.  透明度断点 stop-opacity:能够设置从 0(彻底透明)到 1(彻底不透明)的数值。

咱们须要记住应用渐变遮罩的伪类已经经过 scaleY(-1) 属性镜像过来了,这意味着渐变遮罩的底部在视觉上是顶端。所以渐变是从顶部(视觉下端)的彻底透明到底部(视觉上端)的 .7

由于渐变是从上到下,因此第一个断点彻底透明的。

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 Ana Tudor (@thebabydino) on CodePen.

SVG渐变的问题

在咱们的例子中,由于遮罩渐变是垂直的因此看起来很简单。可是若是渐变不是垂直、水平或者从一个角到另外一个角怎么办?若是咱们想要一个特定角度的渐变怎么办?

SVG中 有一个 gradientTransform 属性,它能够经过指定 x1y1 , 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 的渐变,咱们设置 y1100%,  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 Ana Tudor (@thebabydino) 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 Ana Tudor (@thebabydino) 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 Ana Tudor (@thebabydino) on CodePen.

最后一步就是用 mask 给反射添加渐变。它的方法及代码和以前都是同样的,咱们再也不赘述。全部的代码都在下面的 Pen 中

See the Pen bar loader 2.3.4 fading the reflection by Ana Tudor (@thebabydino) 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 Ana Tudor (@thebabydino) on CodePen.

咱们也尝试在 Firefox 中执行动画。可是,若是咱们把动画添加到以前在 Firefox 中运行良好的代码中,好像出现了一些问题。

在Firefox中使用element()和mask方法作的动画雏形

 

这里出现了一些问题,下面的demo能够在Firefox中实时检测:

See the Pen bar loader 3.2.1 adding animation by Ana Tudor (@thebabydino) 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 Ana Tudor (@thebabydino) 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 Ana Tudor (@thebabydino) 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 Ana Tudor (@thebabydino) on CodePen.

最后的思考

咱们须要一个更好的跨浏览器解决方案。我相信制做物体的反射并不须要像咱们在这个例子中同样复制全部的子元素。为了制做能够放置在图像背景 background 上的渐变反射,咱们不能替换成 SVG 的方案(其自身也有不少问题)。

哪种方案更好? -webkit-box-reflect 仍是 element()mask ?我也不知道。我我的喜欢同时使用。

虽然使用 :reflection 伪类元素 看上去很合理,可是我曾经确信我不想使用额外的元素制做反射。可是如今有比不用插入额外元素更让我喜欢的事情。使用 element() 能够在不一样方向上自由建立多个反射,以及用不一样的方式变换反射,好比 3D 旋转或者倾斜。这正是我喜欢它的缘由。并且用 SVG 作遮罩意味着咱们能够在反射上应用更复杂的遮罩同时得到更酷的效果。

另外一方面,能力越强,责任越大。也许你没有时间去接触强大功能背后的复杂细节。有时你只是想要一个简单的方法来得到一个简单的结果。

相关文章
相关标签/搜索