特别声明:此篇文章内容来源于@ANA TUDOR的《1 HTML Element + 5 CSS Properties = Magic!》一文。
假设我告诉你,我可以使用一个HTML元素和五个CSS属性实现下图的效果。而且这个效果没有使用任何一行SVG代码,也没有使用图像(只是在元素上使用了设置了一个背景图片,只是为了表明这个元素有一些透明的部分),同样也没有使用JavaScript代码。你一定会觉得很神奇,对吧!有好奇之心,对于我们做前端的同学而言,应该一直都有,只有这样才能做出很多我们一直以为实现不了的效果,比如接下来要介绍的内容。
这篇文章将解释如何实现这个效果,然后展示如何通过添加一些动画来让效果变得更有趣。
CSS中的渐变射线
假设在HTML中刚好有一个元素:
在CSS中,给这个元素设置一个尺寸,并且给它添加一个,以便我们能看到它。同时使用把这个元素变成一个圆形。
.rays{
width:80vmin;height:80vmin;border-radius:50%;background:linear-gradient(#b53, #f90);
}
看到上面的代码,可能你会纳闷了,不是说五个属性和一个元素能实现文章开头的效果吗?现在都已经用掉四个属性了,只不过得到如下的一个效果。
那么第五个属性是什么呢?其实就是带有值的一个。
假设我们想要条射线。这意味着我们需要把圆分成份,并且把这这个值赋值给一个变量:,这个值包含了射线和射线间的间距。如下图所示:
在这个示例中,我们让射线和射线间的间距相等,也就是射线和间距都是的大小(也就是的一半),但我们完全可以根据自己所需,将其中任意一个变得更宽或更窄。我们希望在不透明部分(射线)的结束位置就是透明部分地起始位置。如果射线的停止位置是,那么这个间隙的起始位置就不会更大。但是,它可以是小的,它可以帮助我们保持简单,意味着我们可以把间距的起始位置设置为。
注意:与线性渐变和径向渐变不同的是,圆锥渐变的停止位置不能是无单位的。它们要么是百分比,要么是角度值。这意味着像使用是不起作用的,我们需要使用(我们也可以使用替换,不管使用哪一个都不重要,重要的是不能是一个无单位的)。
上面的效果在Edge是无效的。
当聊到浏览器支持的时候,有几点需要注意:
Edge还不支持在HTML元素上使用,尽管Edge已把这个功能列入到开发系列当中,而且它已经出现在中,但是到目前为止还没有做何事情。
仅在Blink内核的浏览器中得到了支持,而且也仅是实验性的特性,如果想要在Blink内核的浏览器中查看到效果,同样需要通过或中开启Experimental Web Platform features。Safari也有支持,但到目前为止,Safari仍然依赖于Polyfill,就像Firefox一样。
Webkit内核浏览器中的仍然需要添加前缀。你会认为这没问题,因为我们使用的是Polyfill,而它依赖于,所以,如果我们使用Polyfill,我们需要在它之前引入。不幸的是,这比我们想的要复杂一点。主要是因为的运行需要通过特性检测,而在这种情况下常会失败,这是因为所有的浏览器都支持SVG不带前缀的属性。但是我们在HTML元素上使用了,而Wekit内核浏览器又需要前缀的情部下,又不会添加,所以需要手动去添加:
$nr:20;$p:100%/$nr; $m: repeating-conic-gradient(#000 0% .5*$p, transparent 0% $p); .rays { -webkit-mask: $m; mask: $m;}
我想我们也可以使用Autoprefixeer,就算是我们需要使用,但总感觉使用这两种方法有点像是在用猎枪打死一只苍蝇一样。
添加动画
在Blink浏览器中已支持了了,这样一来咱们就可以使用CSS自定义属性来替代Sass的变量(如果使用了Polyfill,那是不可以使用CSS自定义属性的)。在Blink内核的浏览器中使用Houdini可以让CSS自定义属性动态变化。
为了添加动画部分的代码,我改变了中的渐变,给值使用了CSS自定义属性。
$m: repeating-conic-gradient( rgba(#000, var(--a)) 0% .5*$p,rgba(#000, calc(1 - var(--a))) 0% $p);
然后我们通过注册一个自定义属性:
CSS.registerProperty({
name:'--a', syntax:'', initialValue:1;})
最后在CSS中添加一个:
.rays{
animation:a2s linear infinite alternate;
}
@keyframes a{to{--a:}
}
最后的效果如下:
效果看起来还不太好。但是,我们可以通过使用多个值来让效果更好一些:
$m: repeating-conic-gradient( rgba(#000,var(--a0))%, rgba(#000,var(--a1)).5*$p, rgba(#000,var(--a2))%, rgba(#000,var(--a3)) $p);
下一步是注册这些自定义属性:
for(let i = 0; i ', initialValue: 1 - ~~(i/2) })}
最后,在CSS中调整:
.rays { animation: a2s infinite alternate; animation-name: a0, a1, a2, a3; animation-timing-function: cubic-bezier(.57,.05,.67,.19)/* easeInCubic */, cubic-bezier(.21,.61,.35,1);/* easeOutCubic */}@for$ifromto4{ @keyframes a#{$i} {to { --a#{$i}: #} }}
注意,由于我们将值设置为自定义属性,所以我们需要插入函数。
这个时候你看到的效果如下:
现在效果看起来蛮不错了,但我们肯定还能做得更好,不是吗?
让我们试着用CSS自定义属性来表示射线和间距的停止位置:
$m: repeating-conic-gradient(#000 0% var(--p), transparent 0% $p);
接来注册另一个自定义属性:
CSS.registerProperty({
name:'--p', syntax:'
', initialValue:'0%'})
我们在CSS的中使用这个自定义属性:
.rays{
animation:p .5s linear infinite alternate}
@keyframes p{to{--p:#{$p}} }
在这种情况下,效果更完美了。
但是我们仍然可以通过在每次迭代之间水平地翻转整个东西来增加它的趣味性,这样它就会一直翻转到相反的部分。这意味着,当从到时和当从到时,它是不会翻转。
在CSS中可以通过可以让一个元素进行水平翻转。由于我们希望在第一次迭代结束时应用这个翻转,然后在第二(反转)结束时删除它。这样我们可以在一个关键帧动画中使用它,并且配合时间函数和两倍的。
$t:.5s;
.rays{
animation:p $t linear infinite alternate, s2*$tsteps(1)infinite;}
@keyframes p{to{--p:#{$p}} }
@keyframes s{ 50%{transform:scalex(-1);}}
现在我们终于有一个看起来非常酷的效果了:
渐变射线和波纹
为了得到光线和波纹(涟漪)的效果,我们需要在上添加第二个渐变属性::
$nr:20;
$p:100% /$nr;
$stop-list:#000 0% .5*$p, transparent 0% $p;
$m: repeating-conic-gradient($stop-list), repeating-radial-gradient(closest-side,$stop-list);.rays-ripples { mask:$m;}
遗憾的是,使用多个停止位置只在Blink内核浏览器中可用,并且是要开启了实验Web平台特性标记。虽然在HTML元素中使用时,的Polyfill会覆盖,但是不支持原生的(Firefox、Safari、Blink浏览器没有启有标记),但在这些浏览器中,对于部分到目前没有相应的解决方案。
这意味着,我们不得不在代码中做一些重复的事情:
$nr:20;
$p:100% /$nr;
$stop-list:#000, #000 .5*$p, transparent 0%, transparent $p;
$m: repeating-conic-gradient($stop-list), repeating-radial-gradient(closest-side,$stop-list);.rays-ripples { mask:$m;}
虽然接近我们想要的效果,但还是没到那一步:
为了得到我们想要的效果,需要使用属性,并且将其设置:
$m: repeating-conic-gradient($stop-list) exclude, repeating-radial-gradient(closest-side,$stop-list);
注意,目前只有Firefox 53+ 支持了,但是当它最终支持HTML元素的时,Edge应该加入进来了。
如果你认为它看起来射线之间的间隙不相等,那是对的。这主要是由于Polyfill引起的问题。
添加动画
由于现在只有 Firefox 53+中才能运行,而Firefox还不支持,因此不能将CSS自定义属性用于中(因为Fiefox仍然要借助于Polyfill,而有Polyfill的时候是不能使用CSS自定义属性)。但是可以在中使用CSS自定义属性,即使我们不能使用CSS关键帧来控制动画,我们也可以使用JavaScript来控制。
因为我们现在把CSS自定义属性用于,但不能用于(正如XOR效应也同样用于,目前只有Firefox支持,而Firefox又不支持原生的,所以会用到Polyfill来做降级处理,但Polyfill又不支持CSS自定义属性)。因此我们不能在的渐变中使用相同的。
但是,如果在没有一个通用的的情况下要重写,那我们就可以利用这个机会使用不同的停止位置来实现两个渐变:
//forconic gradient
$nc: 20;
$pc: 100%/$nc;//forradial gradient
$nr: 10;
$pr: 100%/$nr;
在中有一个CSS自定义属性,就像第一射线动画的示例。我们还引入了和两个CSS自定义属性,这是因为我们不能有多个停止位置,以及我们想尽量避免重复:
$m: repeating-conic-gradient(#000 .5*$pc, transparent 0% $pc) exclude,repeating-radial-gradient(closest-side,var(--c0),var(--c0).5*$pr,var(--c1),var(--c1)$pr);body { --a:;}.xor{ --c0:#;--c1:#;mask:$m;}
自定义属性是我们来回动画的(从到,然后再回到),并使用一点原生的JavaScript来实现这个效果。我们首先设置动画发生的帧数,当前帧索引和当前动画方向:
constNF =50;letf =, dir =1;
在函数中,我们更新当前帧索引,然后将当前的进度值()设置为当前的值。如果已经达到的位置,我们就需要改变方向。然后在下次刷新时再次调用函数。
这就是JavaScript全部内容。现在可以看到一个生动的效果:
这是一个线性动画,值被设置为。但是,我们可以将时间函数更改变其他的,正如我在前面的文章中所解释的那样,使用JavaScript来模拟CSS的时间函数。
例如,如查我们想要一个的时间函数,将值设置为而不是,下面就是函数:
functioneaseIn(k, e = 1.675){returnMath.pow(k, e)}
领取专属 10元无门槛券
私享最新 技术干货