>웹 프론트엔드 >HTML 튜토리얼 >CSS 반영에 대한 연구 생각

CSS 반영에 대한 연구 생각

WBOY
WBOY원래의
2016-09-25 09:24:181535검색

원본 주소: https://css-tricks.com/state-css-reflections

번역자: nzbin

알림: 데모의 호환성으로 인해 Firefox를 권장합니다. 글이 길고 복잡하며 다소 어렵습니다. 저의 지식이 부족하고 시간이 부족하여 번역된 내용이 부적절하고 모호할 수 있습니다. 누구나 수정을 환영합니다.

최근에 순수 CSS로 만든 그라데이션 반사가 있는 3D 회전 수직 막대인 codePen에서 이 로더를 보았습니다. 각 수직 막대에 대한 요소를 만든 다음 각 요소를 복제하여 반사를 만든 다음 마지막으로 반사에 그라데이션 배경을 추가하여 페이드 효과를 만드는 방식으로 만들어집니다. 약간 복잡하게 들리며 그라데이션 배경으로 페이드 효과를 만드는 것은 단색이 아닌 배경에서는 작동하지 않습니다. 더 나은 CSS 방법이 있습니까?

대답은 '예'와 '아니오'입니다. '예'는 방법이 있기 때문에 '아니오'는 일부 방법이 아직 존재하지 않기 때문입니다. 안타깝게도 이 코드는 전처리기(주로 루프를 통해 압축 기능을 구현함)를 사용하여 약간만 압축할 수 있습니다. 캔버스를 사용하지 않고 현재 버전의 주요 브라우저와 호환되도록 하려면 수직 막대를 복사하여 반사를 만들고 그라데이션 배경을 통해 페이드 효과를 만드는 방법이 여전히 가장 좋은 방법입니다.

이 기사에서는 기존의 성찰 방법을 살펴보고 가능한 해결 방법, 브라우저 간 문제 및 내 생각의 예를 제공합니다.

데모 기본 설정

#세로 막대 요소 생성

먼저 .loader 요소를 만들고 그 안에 10개의 .bar 요소

를 만듭니다.
HTML
<span style="color: #0000ff;"><</span><span style="color: #800000;">div </span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">='loader'</span><span style="color: #0000ff;">></span>
  <span style="color: #0000ff;"><</span><span style="color: #800000;">div </span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">='bar'</span><span style="color: #0000ff;">></</span><span style="color: #800000;">div</span><span style="color: #0000ff;">></span>
  <span style="color: #008000;"><!--</span><span style="color: #008000;"> repeat to create 9 more bars </span><span style="color: #008000;">--></span>
<span style="color: #0000ff;"></</span><span style="color: #800000;">div</span><span style="color: #0000ff;">></span>

같은 내용을 여러 번 작성하는 것은 고통스러운 일이므로, 이 경우 전처리기를 사용하면 쉽습니다. 여기서는 Haml 템플릿을 사용하지만 일부 사람들은 다른 템플릿을 선택할 수도 있습니다.

<span style="color: #000000;">.loader
  - 10.times do
  .bar</span>

절대 위치 지정을 통해 모든 요소를 ​​뷰 중앙에 배치합니다. 대부분의 경우 top: 50% 을 사용하지만, 이제 bottom: 50% 을 사용하면 후속 작업이 더 복잡해집니다. 단순한.

CSS
<span style="color: #800000;">div </span>{<span style="color: #ff0000;">
  position</span>:<span style="color: #0000ff;"> absolute</span>;<span style="color: #ff0000;">
  bottom</span>:<span style="color: #0000ff;"> 50%</span>;<span style="color: #ff0000;"> left</span>:<span style="color: #0000ff;"> 50%</span>;
}

세로바에 너비높이를 설정하고, 를 하나 더 설정하여 배경

SCSS
<span style="color: #800000;">$bar-w: 1.25em;
$bar-h: 5 * $bar-w;

.bar </span>{<span style="color: #ff0000;">
  width</span>:<span style="color: #0000ff;"> $bar-w</span>;<span style="color: #ff0000;"> <br>  height</span>:<span style="color: #0000ff;"> $bar-h</span>;<span style="color: #ff0000;">
  background</span>:<span style="color: #0000ff;"> currentColor</span>;
}

我们希望竖条的底部贴合视图的水平中心线。设置 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
<span style="color: #800000;">$n: 10;

@for $i from 0 to $n </span>{<span style="color: #ff0000;">
  .bar</span>:<span style="color: #0000ff;">nth-child(#{$i + 1</span>}<span style="color: #800000;">) </span>{<span style="color: #ff0000;">
  margin-left</span>:<span style="color: #0000ff;"> ($i - .5 * $n) * $bar-w</span>;
  }<span style="color: #800000;">
}</span>

 为了看清楚竖条的边界,我们给它一个 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
<span style="color: #800000;">$c: #63a6c1 #1e3f57; // 1st = light 2nd = dark

@for $i from 0 to $n </span>{<span style="color: #ff0000;">
  // list of mix() arguments for current bar
  $args</span>:<span style="color: #0000ff;"> append($c, $i * 100% / ($n - 1))</span>;<span style="color: #ff0000;">

  .bar</span>:<span style="color: #0000ff;">nth-child(#{$i + 1</span>}<span style="color: #800000;">) </span>{<span style="color: #ff0000;">
  background</span>:<span style="color: #0000ff;"> mix($args...)</span>;
  }<span style="color: #800000;">
}</span>

现在这些竖条看起来就和原始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
<span style="color: #800000;">.loader </span>{<span style="color: #ff0000;">
  -webkit-box-reflect</span>:<span style="color: #0000ff;"> below 0 linear-gradient(rgba(#fff), rgba(#fff, .7))</span>;
}

但是在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
<span style="color: #800000;">$loader-w: $n * $bar-w;

.loader </span>{<span style="color: #ff0000;">
  width</span>:<span style="color: #0000ff;"> $loader-w</span>;<span style="color: #ff0000;"> height</span>:<span style="color: #0000ff;"> $bar-h</span>;<span style="color: #ff0000;">
  box-shadow</span>:<span style="color: #0000ff;"> 0 0 0 1px red</span>;
}

我之所以用 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
<span style="color: #800000;">.loader </span>{<span style="color: #ff0000;"> margin-left</span>:<span style="color: #0000ff;"> -.5 * $loader-w</span>; }<span style="color: #800000;">

.bar </span>{<span style="color: #ff0000;"> bottom</span>:<span style="color: #0000ff;"> 0</span>; }

修正位置之后的样子如下:

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
<span style="color: #800000;">.loader::after </span>{<span style="color: #ff0000;">
  position</span>:<span style="color: #0000ff;"> absolute</span>;<span style="color: #ff0000;">
  top</span>:<span style="color: #0000ff;"> 0</span>;<span style="color: #ff0000;"> right</span>:<span style="color: #0000ff;"> 0</span>;<span style="color: #ff0000;"> bottom</span>:<span style="color: #0000ff;"> 0</span>;<span style="color: #ff0000;"> left</span>:<span style="color: #0000ff;"> 0</span>;<span style="color: #ff0000;">
  box-shadow</span>:<span style="color: #0000ff;"> 0 0 0 2px currentColor</span>;<span style="color: #ff0000;">
  color</span>:<span style="color: #0000ff;"> crimson</span>;<span style="color: #ff0000;">
  content</span>:<span style="color: #0000ff;"> 'REFLECTION'</span>;
}

为了在最终效果中看清伪类的边界和方向,我们设置了一些暂时性的样式,我们想让它是颠倒的。

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
<span style="color: #800000;">.loader::after </span>{<span style="color: #ff0000;">
  transform-origin</span>:<span style="color: #0000ff;"> 0 100%</span>;<span style="color: #ff0000;">
  transform</span>:<span style="color: #0000ff;"> scaleY(-1)</span>;<span style="color: #ff0000;">
  background</span>:<span style="color: #0000ff;"> -moz-element(#loader)</span>;
}

注意:由于特别的原因我们使用 .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
<span style="color: #800000;">mask: url(#fader);</span>

 #fader 元素是一个包含长方形的SVG mask 元素。

SVG
<span style="color: #0000ff;"><</span><span style="color: #800000;">svg</span><span style="color: #0000ff;">></span>
  <span style="color: #0000ff;"><</span><span style="color: #800000;">mask </span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">='fader' </span><span style="color: #ff0000;">maskContentUnits</span><span style="color: #0000ff;">='objectBoundingBox'</span><span style="color: #0000ff;">></span>
    <span style="color: #0000ff;"><</span><span style="color: #800000;">rect </span><span style="color: #ff0000;">width</span><span style="color: #0000ff;">='1' </span><span style="color: #ff0000;">height</span><span style="color: #0000ff;">='1'</span><span style="color: #0000ff;">/></span>
  <span style="color: #0000ff;"></</span><span style="color: #800000;">mask</span><span style="color: #0000ff;">></span>
<span style="color: #0000ff;"></</span><span style="color: #800000;">svg</span><span style="color: #0000ff;">></span>

我们可以用Haml模板压缩一下

Haml
<span style="color: #000000;">%svg
  %mask#fader(maskContentUnits='objectBoundingBox')
    %rect(width='1' height='1')</span>

但是,如果我们加上以上代码,我们的反射倒影消失了,在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
<span style="color: #000000;">%linearGradient#grad(x2='0%' y2='100%')
  %stop(offset='0' stop-color='#fff' stop-opacity='0')
  %stop(offset='1' stop-color='#fff' stop-opacity='.7')</span>

添加线性渐变之后,在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
<span style="color: #800000;">background-image: linear-gradient(0deg, #e18728 50%, #d14730 0);</span>

如果你还不明白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
<span style="color: #000000;">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')</span>

编辑注:我问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
<span style="color: #800000;">background-image: linear-gradient(90deg, #e18728 50%, #d14730 0);</span>

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
<span style="color: #000000;">linearGradient#g(y1='100%' x2='0%' y2='0%' gradientTransform='rotate(90 .5 .5)')
  // same stops as before</span>

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
<span style="color: #000000;">- 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 </span><span style="color: #0000ff;"><</span><span style="color: #800000;"> n</span><span style="color: #ff0000;">; i++) {
      - var x </span><span style="color: #0000ff;">= (i </span><span style="color: #ff0000;">- .5 * n) * bar_w;
      use(xlink:href</span><span style="color: #0000ff;">='#bar' </span><span style="color: #ff0000;">x</span><span style="color: #0000ff;">=x)
    </span><span style="color: #ff0000;">- }</span>

 以上代码的效果如下:

See the Pen bar loader 2.3.1 creating and positioning SVG bars by Zongbin Niu (@nzbin) on CodePen.

现在我们已经创建了所有竖条,我们想把svg元素的位置调整的更准确而且我们使用flexbox属性。同时我们也和之前一样给竖条添加渐变色。我们用Sass做这些事情

SCSS
<span style="color: #800000;">$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 </span>{<span style="color: #ff0000;">
  display</span>:<span style="color: #0000ff;"> flex</span>;<span style="color: #ff0000;">
  justify-content</span>:<span style="color: #0000ff;"> center</span>;<span style="color: #ff0000;">
  margin</span>:<span style="color: #0000ff;"> 0</span>;<span style="color: #ff0000;">
  height</span>:<span style="color: #0000ff;"> 100vh</span>;
}<span style="color: #800000;">

svg </span>{<span style="color: #ff0000;">
  align-self</span>:<span style="color: #0000ff;"> center</span>;<span style="color: #ff0000;">
  width</span>:<span style="color: #0000ff;"> $loader-w</span>;<span style="color: #ff0000;"> height</span>:<span style="color: #0000ff;"> $loader-h</span>;
}<span style="color: #800000;">

@for $i from 0 to $n </span>{<span style="color: #ff0000;">
  $args</span>:<span style="color: #0000ff;"> append($c, $i * 100%/($n - 1))</span>;<span style="color: #ff0000;">

  [id='loader'] use</span>:<span style="color: #0000ff;">nth-child(#{$i + 1</span>}<span style="color: #800000;">) </span>{<span style="color: #ff0000;">
    fill</span>:<span style="color: #0000ff;"> mix($args...)</span>;
  }<span style="color: #800000;">
}</span>

 添加以上代码后的效果如下:

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
<span style="color: #800000;">@keyframes bar </span>{<span style="color: #ff0000;">
  0% {
  transform</span>:<span style="color: #0000ff;"> rotate(-.5turn) rotateX(-1turn)</span>;
  }<span style="color: #800000;">
  75%, 100% </span>{<span style="color: #ff0000;"> transform</span>:<span style="color: #0000ff;"> none</span>; }<span style="color: #800000;">
}</span>

 所有的竖条都是同样的动画:

CSS
<span style="color: #800000;">animation: bar 3s cubic-bezier(.81, .04, .4, .7) infinite;</span>

 我们只是给循环的竖条添加了不同的延迟时间。

SCSS
<span style="color: #800000;">animation-delay: $i*50ms;</span>

 因为我们希望旋转的竖条具有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
<span style="color: #800000;">$loader-w: ($n + 1) * $bar-w + $bar-h;</span>

 但是我们对于其余的两个问题就束手无策了。当竖条进行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
<span style="color: #000000;">- 2.times do
  .loader
    - 10.times do 
      .bar</span>

 竖条的样式和之前一样,我们使用 scale(-1) 来镜像第二个loader元素。

CSS
<span style="color: #800000;">.loader:nth-child(2) </span>{<span style="color: #ff0000;">
  transform</span>:<span style="color: #0000ff;"> scaleY(-1)</span>;
}

 我们添加竖条动画后得到如下结果:

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
<span style="color: #800000;">$bgc: #eee;
$cover-h: $bar-w + $bar-h;
$cover-w: $n * $bar-w + $cover-h;

html </span>{<span style="color: #ff0000;"> background</span>:<span style="color: #0000ff;"> $bgc</span>; }<span style="color: #800000;">

.loader:nth-child(2)::after </span>{<span style="color: #ff0000;">
  margin-left</span>:<span style="color: #0000ff;"> -.5 * $cover-w</span>;<span style="color: #ff0000;">
  width</span>:<span style="color: #0000ff;"> $cover-w</span>;<span style="color: #ff0000;"> height</span>:<span style="color: #0000ff;"> $cover-h</span>;<span style="color: #ff0000;">
  background</span>:<span style="color: #0000ff;"> linear-gradient($bgc $bar-w, rgba($bgc, .3))</span>;<span style="color: #ff0000;">
  content</span>:<span style="color: #0000ff;"> ''</span>;
}

최종 결과는 다음과 같습니다.

CodePen에서 Zongbin Niu(@nzbin)의 표지로 페이딩을 에뮬레이션하는 펜 바 로더 3.3.2를 참조하세요.

최종 생각

더 나은 크로스 브라우저 솔루션이 필요합니다. 나는 객체의 반사를 만드는 데 이 예제에서 했던 것처럼 모든 하위 요소를 복사할 필요가 없다고 생각합니다. 배경 이미지에 배치할 수 있는 그라데이션 반사를 생성하기 위해 SVG 솔루션을 대체할 수 없습니다(자체적인 문제가 있음).

어떤 솔루션이 더 좋나요? -webkit-box-reflect 또는 element() + mask ? 나도 모른다. 저는 개인적으로 두 가지를 모두 사용하는 것을 좋아합니다.

:reflection 의사 클래스 요소를 사용하는 것이 합리적으로 보였지만 반사를 생성하기 위해 추가 요소를 사용하고 싶지 않다고 확신했습니다. 하지만 이제는 추가 요소를 삽입하지 않는 것보다 더 마음에 드는 것이 있습니다. element()를 사용하면 서로 다른 방향으로 여러 반사를 자유롭게 만들고 3D 회전이나 기울기와 같은 다양한 방법으로 반사를 변환할 수 있습니다. 그것이 바로 내가 그것을 좋아하는 이유입니다. 그리고 마스킹에 SVG를 사용한다는 것은 반사에 더 복잡한 마스크를 적용하고 더 멋진 효과를 얻을 수 있다는 것을 의미합니다.

반면, 능력이 크면 책임감도 커집니다. 어쩌면 강력한 기능 뒤에 숨겨진 복잡한 세부정보를 살펴볼 시간이 없을 수도 있습니다. 때로는 간단한 결과를 얻기 위한 간단한 방법을 원할 때도 있습니다.

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.