CSS 反射状态

Avatar of Ana Tudor
Ana Tudor 发布

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 $200 免费信用额度!

我最近在 CodePen 上 看到了这个加载器,这是一个纯 CSS 3D 旋转的条形集,带有淡出的反射。它是通过为每个条形使用一个元素,然后复制每一个元素来创建反射,最后添加一个渐变覆盖来创建淡出效果实现的。这听起来有点像用左脚的脚趾抓右耳后面!更不用说用于淡出效果的渐变覆盖方法不适用于非纯色背景。难道没有更好的方法用 CSS 来做到这一点吗?

答案是:“是”和“否”。 “是”,有一些方法确实可以奏效,而“否”,它们还没有真正出现。遗憾的是,虽然可以使用预处理器稍微压缩一下代码(除了可以在循环中生成的代码之外,没有太多压缩),但对于反射来说,复制所有条形并使用渐变覆盖来实现淡出效果的方法仍然是最佳方法,如果我们不想使用 canvas,并且希望结果能够在所有主流浏览器的当前版本中正常工作。

这篇文章将探讨我们今天创建反射的选项,说明“几乎”可行的解决方案,以及跨浏览器问题如何造成困扰,最后讨论我对应该采取的措施的想法。

演示的基本设置

在我们开始谈论反射之前,让我们看看如何创建、定位和阴影条形,因为这部分对所有浏览器来说都是一样的。

创建条形

首先,我们创建一个 .loader 包装器元素,其中包含 10 个 .bar 元素。

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

在许多情况下,编写相同的内容多次非常麻烦,因此在这样的情况下使用预处理器更容易。我们这里使用 Haml,但任何其他的预处理器都可以。

.loader
  - 10.times do
  .bar

我们将所有这些元素绝对定位,从视窗的中间开始。在大多数情况下,我们使用 top: 50%,但在这种情况下,如果我们使用 bottom: 50%,以后会更方便。

div {
  position: absolute;
  bottom: 50%; left: 50%;
}

我们为条形确定一个 widthheight,并为它们设置一个 background,这样我们就可以看到它们。

$bar-w: 1.25em;
$bar-h: 5 * $bar-w;

.bar {
  width: $bar-w; height: $bar-h;
  background: currentColor;
}

我们希望条形的底部边缘与视窗的中间线重合(将视窗分成两个相等的部分),并且由于我们使用了 bottom: 50%,所以我们已经有了它。

此时,我们的条形都堆叠在一起,它们的左边缘位于将视窗分成两个相等部分(左和右)的垂直线上,它们的底部边缘位于将视窗分成两个相等部分(上和下)的水平线上。

定位条形

我们需要将它们定位,使得第一个(最左边的)条形的左边缘和最后一个(最右边的)条形的右边缘与将视窗分成两个相等部分的垂直线等距。这个距离始终是条形数量($n)的一半乘以条形 width$bar-w)。原始演示使用的是纯 CSS,但我们现在使用 Sass 来减少代码量。

这意味着,从现在所有条形所在的 position 开始,我们需要将第一个条形向左移动 .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

一般来说,如果我们认为 $i 是当前条形的 0 为基准的索引,那么这个 $i 条形的 margin-left 值是 -.5 * $n * $bar-w + $i * $bar-w,可以压缩为 ($i - .5 * $n) * $bar-w

这允许我们使用 Sass 循环来定位条形

$n: 10;

@for $i from 0 to $n {
  .bar:nth-child(#{$i + 1}) {
  margin-left: ($i - .5 * $n) * $bar-w;
  }
}

我们还为它们设置一个 box-shadow,这样我们就可以看到一个条形在哪里结束,下一个条形在哪里开始。

阴影条形

条形的背景从最左边的条形开始是深蓝色 (#1e3f57),到最右边的条形结束是浅蓝色 (#63a6c1)。这听起来像是 Sass mix() 函数 的工作!第一个参数是浅蓝色,第二个参数是深蓝色,第三个参数(称为相对权重)是最终混合中包含浅蓝色的数量(以 % 为单位)。

对于第一个条形,这个数量是 0% – 结果中 0% 的浅蓝色,所以这个结果只包含深蓝色。

对于最后一个条形,这个数量是 100% – 最终结果中 100% 的浅蓝色(这也意味着 0% 的较暗色调),这将使背景变为浅蓝色。

对于其他条形,我们需要均匀分布的中间值。如果我们有 $n 个条形,第一个条形在 0%,最后一个条形在 100%,那么我们需要将它们之间的间隔分成 $n - 1 个相等的间隔。

一般来说,索引为 $i 的条形的相对权重是 $i * 100% / ($n - 1),这意味着我们需要添加以下代码

$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...);
  }
}

现在条形看起来与原始演示中一样。

探索反射的选项

WebKit 浏览器:-webkit-box-reflect

哦,不,这是一个非标准属性!我不知道为什么它没有成为标准。我第一次在 Safari 中看到它的时候,甚至还没有听说过 CSS。但是,对于 WebKit 浏览器来说,它确实可以完成任务,而且它做得很好!投入了大量的努力。它易于使用,并且不会在不支持的浏览器中出现任何问题,只是不会显示反射。

让我们看看它是如何工作的。它所接受的值有三个部分

  • 一个方向,可以是 belowleftaboveright 关键字中的任何一个
  • 一个可选的偏移量,指定反射应该从元素边缘开始的距离(这是一个 CSS 长度值)
  • 一个可选的图像遮罩(可以是 CSS 渐变)

以下交互式演示说明了这一点(单击方向、偏移量、渐变角度、停止 alpha 和偏移量以更改它们)

请注意,linear-gradient() 可以有更多停止,也可以替换为 radial-gradient()

在我们的案例中,首先想到的是在 .loader 元素上添加这个属性

.loader {
  -webkit-box-reflect: below 0 linear-gradient(rgba(#fff), rgba(#fff, .7));
}

但是,如果我们在 WebKit 浏览器中测试它,我们没有看到任何反射!

这里发生了什么?我们将所有元素绝对定位,并且没有在包含条形的 .loader 元素上设置任何显式尺寸。这使得它成为一个 0x0 元素 – 零 width,零 height

因此,让我们为它设置一些显式尺寸,一个等于条形高度 ($bar-h) 的 height,以及一个足以容纳所有条形 ($n * $bar-w) 的 width。我们还暂时为它设置一个 box-shadow,这样我们就可以清楚地看到它的边界

$loader-w: $n * $bar-w;

.loader {
  width: $loader-w; height: $bar-h;
  box-shadow: 0 0 0 1px red;
}

我更喜欢使用 box-shadow 而不是 outline 来突出显示元素的边界,因为 当子元素溢出父元素时,outline 在不同浏览器之间表现不一致.

box-shadow 和 outline 在 WebKit 浏览器和 Edge(上)中的行为与 Firefox(下)中的行为有何不同 – 当子元素溢出时,outline 会给出不同的结果

添加上述代码的结果可以在以下 Pen 的 WebKit 浏览器中看到。

如果您不在 WebKit 浏览器中,以下是它的外观

在 Chrome 中,在显式调整 .loader 元素的大小后的结果的屏幕截图

我们现在可以看到加载器的边界,并且可以看到一些反射,但是东西不再正确地定位了。我们希望加载器在水平方向上处于正中间,因此我们将它向左移动其width的一半。我们还希望条形的底部与它们父级的底部重合,因此我们在它们上设置bottom: 0

.loader { margin-left: -.5 * $loader-w; }

.bar { bottom: 0; }

这修复了定位问题。以下是它现在的样子

Firefox: element() + mask

使用element()创建反射

element()函数(仍在开发中,到目前为止,只有 Firefox 使用 -moz- 前缀实现)为我们提供了一个图像值,我们应该能够在可以使用实际图像的任何地方使用它(适用于 background,适用于 border-image,但似乎不适用于伪元素 content 的值 - 参见错误 1285811)。它接受一个参数,该参数是我们希望作为 backgroundborder-image 显示的元素的 id 选择器。这使我们能够执行一些邪恶的操作,例如使用控件图像作为背景。但如果我们想在 Firefox 中将元素反射,它也会派上用场。

关于 element() 函数需要了解的一件非常重要的事情是,它不是递归的 - 我们不能通过将元素用作它们自己的背景来创建分形。这使得它可以安全地用于加载器伪元素以创建反射,因此我们不需要使用额外的元素。

好的,让我们看看如何做到这一点。首先,我们为加载器元素提供一个 id(假设是明显的 loader)。接下来是样式,我们从 WebKit 情况下的最终演示中使用的完全相同的 CSS 开始。然后,我们在加载器上添加一个 ::after 伪元素,绝对定位并完全覆盖它。

.loader::after {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  box-shadow: 0 0 0 2px currentColor;
  color: crimson;
  content: 'REFLECTION';
}

我们还设置了一些额外的临时样式,这样我们就可以清楚地了解此伪元素的边界和方向,因为在最终形式中,我们希望它上下颠倒

现在,我们需要沿其底部边缘反射我们的 ::after 伪元素。为此,我们使用具有适当选择的 transform-originscaleY() 变换。以下交互式演示说明了不同比例因子和变换原点如何进行定向缩放

请注意,比例因子和 transform-origin 的值可以超出此演示施加的限制。

在我们的案例中,我们需要一个 scaleY(-1) 和一个在 ::after 伪元素底部边缘线上的 transform-origin

使用具有适当 transform-originscaleY(-1) 变换向下反射元素

我们将此添加到代码中,并将 #loader 设置为其 ::after 伪元素的背景,使用 element() 函数(带前缀,因为这是目前唯一支持的方式)。

.loader::after {
  transform-origin: 0 100%;
  transform: scaleY(-1);
  background: -moz-element(#loader);
}

请注意,我们使用 .loader 作为选择器以获得特异性,并将 #loader 作为 element() 函数的参数,因为这需要是一个 id 选择器。

添加上述代码的结果可以在以下 Pen 中看到(仅限 Firefox)

对于在其他浏览器上阅读本文的所有人,以下是一张截图,显示了它的样子

在 Firefox 中使用 element() 函数进行反射的结果
使用 mask 淡化反射

我们以与 WebKit 案例中相同的方式淡化反射:使用蒙版。在这种情况下,蒙版是 -webkit-box-reflect 值的组成部分。在这种情况下,我们谈论的是 CSS mask 属性,它接受一个 SVG 引用作为值

mask: url(#fader);

我们的 #fader 元素是一个包含矩形的 SVG mask 元素。

<svg>
  <mask id='fader' maskContentUnits='objectBoundingBox'>
    <rect width='1' height='1'/>
  </mask>
</svg>

我们可以使用 Haml 将其压缩一下

%svg
  %mask#fader(maskContentUnits='objectBoundingBox')
    %rect(width='1' height='1')

但是,如果我们真的将上面的代码添加到我们的代码中,我们的反射就会消失 - 这可以通过在 Firefox 中查看以下演示来测试

这是因为,默认情况下,SVG 形状具有实心黑色 fill,完全不透明,同时,我们的蒙版默认情况下是亮度蒙版。因此,为了使反射淡出,我们需要做的是为矩形提供一个 fill,该 fill 是对 SVG linearGradient 的引用。

%rect(width='1' height='1' fill='url(#grad)')

SVG linearGradient 在由 x1y1x2y2 属性指定的两个点之间定义。x1y1 是渐变线起点 (0%) 的坐标,而 x2y2 是此线的终点 (100%) 的坐标。如果这些属性缺失,则分别采用 0%0%100%0%。这些值描述了从应用元素的左上角 (0% 0%) 到右上角 (100% 0%) 的线(因为 gradientUnits 的默认值为 objectBoundingBox),这意味着默认情况下,渐变是从左到右。

但在我们的情况下,我们希望渐变从 topbottom,因此我们将 x2 的值从 100% 更改为 0%,并将 y2 的值从 0% 更改为 100%。这使得渐变向量从应用元素的左上角 (0% 0%) 到左下角 (0% 100%) 的角落。

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

linearGradient 元素内部,我们至少有两个 stop 元素。这些元素具有三个特定属性:offsetstop-colorstop-opacity

  1. offset 可以接受一个 % 值,通常在 0%100% 之间,就像 CSS 渐变一样。它也可以接受一个数字值,通常在 01 之间。
  2. stop-color 可以接受一个关键字、hexrgb()rgba()hsl()hsla() 值。理论上是可以的。实际上,Safari 不支持半透明值,因此如果我们希望在渐变中使用半透明,我们应该依赖于第三个属性…
  3. stop-opacity。这接受一个在 0(完全透明)和 1(完全不透明)之间的值。

我们需要记住,我们正在应用渐变蒙版的加载器伪元素已通过 scaleY(-1) 变换向下反射。这意味着我们的渐变蒙版的底部在视觉上是向上。因此,我们的渐变需要从顶部(视觉上向下)的完全透明过渡到底部(视觉上向上)的 .7 的 alpha。

由于我们的渐变是从上到下,因此第一个停止点是完全透明的。

%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 中想要的结果

此 Pen 展示了它在实践中的效果

SVG 渐变问题

在我们的案例中,事情很简单,因为我们的蒙版渐变是垂直的。但是,那些不是垂直的或水平的,或者不是从一个角到另一个角的渐变呢?如果我们想在某个角度有一个渐变呢?

好吧,SVG 渐变还有一个名为 gradientTransform 的属性,它可以旋转由 x1y1x2y2 属性定义的渐变线。人们可能认为这是一个轻松再现 CSS 角渐变的方法。但是…事情没有那么简单!

让我们考虑从金色到深红色的渐变的例子。为了使事情更清楚,我们在 50% 处为它提供了一个清晰的过渡。最初,我们将此渐变的 CSS 版本的角度设为 0deg。这意味着渐变从底部 (金色) 的 0% 过渡到顶部 (深红色) 的 100%。创建此渐变的 CSS 将是

background-image: 
  linear-gradient(0deg, #e18728 50%, #d14730 0);

如果您不了解 CSS 线性渐变的工作原理,可以查看Patrick Brosset 的这篇文章。

结果可以在下面的 Pen 中看到

为了用 SVG 再现这一点,我们创建一个渐变,其中 y1100%y20%x1x2 具有相同的值(为了简单起见,我们将其设置为 0)。这意味着渐变线从底部垂直向上延伸到顶部。我们还将两个停止偏移都设置为 50%

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 正好位于中间。这可以在以下 Pen 中看到

如果我们希望我们的渐变从左到右,那么在 CSS 渐变的情况下,我们将角度从 0deg 更改为 90deg

background-image: 
  linear-gradient(90deg, #e18728 50%, #d14730 0);

为了用 SVG 渐变获得相同的结果,我们将 gradientTransform 的值更改为 rotate(90 .5 .5)

linearGradient#g(y1='100%' x2='0%' y2='0%'
                 gradientTransform='rotate(90 .5 .5)')
  // same stops as before

到目前为止,一切顺利。用 SVG 复制 CSS 渐变似乎并不那么痛苦。但是,让我们也尝试其他角度。在下面的交互式演示中,我们在左侧有一个 CSS 渐变,在右侧有一个 SVG 版本。紫色线是渐变线,它应该垂直于金色和深红色之间清晰的分隔线。拖动滑块将改变 CSS 和 SVG 情况下的渐变角度。我们可以看到,对于不是 90deg 的倍数的值,有些地方不对劲。

如上图所示,对于非 90deg 倍数的值,我们得不到相同的结果。如果我们设置渐变的元素是正方形,我们会得到相同的结果。这意味着我们可以将渐变设置为更大的正方形元素,然后将其剪裁到我们实际的元素。但是,必须这样做使得使用 element()mask 创建淡出反射的方法变得更加复杂。

边缘:全部使用 SVG?

遗憾的是,上面介绍的两种方法在 Edge 中都无法使用。因此,我们唯一能够在 Edge 中使用且不涉及手动复制每一个条形的解决方案就是放弃我们迄今为止所做的一切,并使用 SVG 重新创建加载器。这具有跨浏览器方法的优势。

基本上,我们创建了一个 SVG 元素,其 viewBox 使其 0 0 点位于正中间。我们定义了一个条形,其底边位于 x 轴上,其左边位于 y 轴上。然后,我们在 #loader 组内根据需要克隆(通过 SVG use 元素)此条形。我们以与之前相同的方式处理这些克隆的定位。

- 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)
    - }

以下 Pen 展示了上述代码的结果

现在我们已经创建了这些条形,我们希望以良好的方式定位 svg 元素,我们使用 flexbox 来做到这一点。我们还希望像以前一样对条形进行阴影处理。我们从 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...);
  }
}

此 Pen 展示了添加上述代码的结果

我们克隆 #loader 组(同样,使用 use 元素)。我们使用 scale(1 -1) 函数反射此克隆,并对其应用掩码,与之前对伪元素所做的操作相同。默认情况下,SVG 元素相对于 SVG 画布的 0, 0 点进行缩放,在本例中,它位于我们加载器的底边上,非常适合将加载器克隆向下反射,我们无需设置 transform-origin

use(xlink:href='#loader' transform='scale(1 -1)')

我们使用 transform 属性而不是 CSS 变换,因为 Edge 不支持 CSS 变换 - 如果你想要它们,请 投票支持

我们现在有了反射,如以下 Pen 中所示

最后一步是使用 mask 使反射淡出。它与之前的方法和代码完全相同,所以我们不再赘述。完整的代码位于此方法的最终 Pen 中,你可以查看以下内容

动画

原始 Pen 中的 CSS 动画是一个非常简单的动画,它以 3D 方式旋转条形

@keyframes bar {
  0% {
  transform: rotate(-.5turn) rotateX(-1turn);
  }
  75%, 100% { transform: none; }
}

所有条形的动画都相同

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

我们只在条形循环内为每个条形添加不同的延迟

animation-delay: $i*50ms;

由于我们以 3D 方式旋转条形,因此我们还在加载器元素上添加了 perspective

但这仅在使用 -webkit-box-reflect 方法的 WebKit 浏览器中按预期工作。

使用 Chrome 中的 -webkit-box-reflect 方法的最终结果的录制

我们还添加了图像背景,只是为了展示它将是什么样子。此仅限 WebKit 的情况下完成的演示是

我们也可以尝试让它在 Firefox 中工作。但是,如果我们将动画代码添加到也在 Firefox 中工作的版本中,则看起来不太对劲

使用 Firefox 中的 element()mask 方法的初始动画版本的录制

我们在这里遇到了一些问题,可以在 Firefox 中进行现场测试

第一个问题是,反射在伪元素的边界之外被截断。我们可以通过增加加载器元素的尺寸(因此,伪元素的尺寸也会增加)来解决这个问题

$loader-w: ($n + 1) * $bar-w + $bar-h;

但是,我们对另外两个问题无能为力 - 反射没有随着条形以 3D 方式旋转而平滑更新,以及 perspective 属性的存在导致条形消失(见错误 1282312)。

使用 Firefox 中的 element() 和 mask 方法的动画版本的录制(带有 perspective)
使用 Firefox 中的 element() 和 mask 方法的动画版本的录制(没有 perspective)

Firefox 的现场测试(你可以打开或关闭 perspective 以查看区别)

那么全 SVG 解决方案呢?嗯,不幸的是,在上述关键帧集中,我们正在为 CSS 3D 变换设置动画。Edge 尚未支持 SVG 元素的 CSS 变换,这就是我们之前依靠 transform 属性创建反射的原因。但是,transform 属性的值严格为 2D,我们无法在没有 JavaScript 的情况下为它们设置动画(有些人可能认为 SMIL,但那是标记呕吐,它从未在 Edge/IE 中得到支持,现在它在 Chrome 中已被弃用)。

因此,目前完全没有办法以跨所有浏览器工作且不复制每一个条形的方式重新创建这种类型的条形加载器演示。我们所能做的就是有两个加载器元素,每个元素都有相同数量的条形

- 2.times do
  .loader
    - 10.times do 
      .bar

条形的样式与以前相同,我们使用 scale(-1) 变换将第二个加载器元素向下反射

.loader:nth-child(2) {
  transform: scaleY(-1);
}

我们添加条形动画,得到以下结果

现在我们需要淡出反射。遗憾的是,我们无法在第二个加载器元素上应用 mask,因为掩码仅在 SVG 元素的情况下才跨浏览器。Edge 尚未支持 HTML 元素的掩码,但你可以 投票以实施它

我们唯一能做的事情是使用第二个加载器(反射的加载器)的渐变覆盖。请注意,这也意味着我们不能使用图像背景。只能使用纯色背景,或者在极少数情况下使用渐变背景。我们从第二个加载器的 ::after 伪元素创建此覆盖,并使其足够大,以覆盖条形,即使它们被旋转。

$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: '';
}

以下 Pen 展示了结果

最后的想法

我们需要一个更好的跨浏览器解决方案。我相信,反射一个元素不应该像我们在这个条形加载器中需要的那样,涉及复制所有它的后代。我们不应该需要切换到 SVG 解决方案(它也带来了自身的问题),仅仅是为了使反射淡出并拥有图像 background

哪种解决方案更好?-webkit-box-reflect 还是 element() + mask?我不知道。我个人希望两者都能跨浏览器使用。

我曾经确信我不想为反射添加额外的元素,尽管 :reflection 伪元素 听起来很合理。但现在我更希望比没有额外元素更好。它是能够在不同方向创建多个反射,并以各种方式变换这些反射的自由,比如在 3D 中旋转它们或倾斜它们。使用 element() 方法可以实现所有这些功能,这就是我喜欢它的原因。更不用说使用 SVG 进行掩码意味着我们可以对这些反射应用更复杂的掩码,并获得更酷的效果。

另一方面,权力越大,责任越大。也许你没有时间去熟悉更强大方法背后的所有复杂性。有时你只是想要一个简单的方法来获得简单结果。