最近 S11 LPL 春季赛开赛,在看比赛的过程中,我发现新赛季的 Ban/Pick 选人阶段,出现了一种新的,有意思的遮罩效果,如下图所示:
当然,它是一个动态的效果,当选人的过程中,会有一种呼吸的效果:
Gif 图有点糊,总的而言,就是一种接近迷雾的遮罩效果。并且,他是能够动态变化的。
本文将探究,在 CSS 中,我们应该如何去实现类似的效果。
实现烟雾化遮罩效果
首先,我们来尝试实现这样一个动态遮罩:
假设没有模糊的边缘,及烟雾化的效果,它其实就是一个渐变:
<div></div>
div { width: 340px; height: 180px; border: 2px solid #5b595b; background: linear-gradient( rgba(229, 23, 49, 1), rgba(229, 23, 49, .9) 48%, transparent 55%, ); }
经由上述代码,我们可得到:
好吧,看着确实平平无奇,我们如何利用它,得到一个雾化的效果呢?
提到烟雾,聪明的同学应该能想到滤镜,当然,是 SVG 的 <feturbulence> 滤镜。
没错,又是它,<feturbulence> 确实太有意思了,我最近的两篇关于它的文章 -- Amazing!!CSS 也能实现烟雾效果?、Amazing!!CSS 也能实现极光? 可以一并阅读。
<feturbulence> 的 type="fractalNoise" 在模拟云雾效果时非常好用。该滤镜利用 Perlin 噪声函数创建了一个图像,能够实现半透明的烟熏或波状图像,用于实现一些特殊的纹理。
这里,我们利用 <feturbulence> 滤镜简单处理一下上述图形:
<div></div> <svg width="0"> <filter id="filter"> <feTurbulence id="turbulence" type="fractalNoise" baseFrequency=".03" numOctaves="20" /> <feDisplacementMap in="SourceGraphic" scale="30" /> </filter> </svg>
CSS 中,可以利用 filter: url() 对对应的元素引入该滤镜:
div { ... filter: url(#smoke); }
作用了滤镜的元素的效果:
由于我给元素加了边框,整个边框也被雾化了,这不是我们想要的,可以使用伪元素改造一下,边框作用于容器,使用伪元素实现渐变,将滤镜作用于伪元素:
div { position: relative; width: 340px; height: 180px; border: 2px solid #5b595b; &::before { content: ""; position: absolute; left: 0; top: 0; right: 0; bottom: 0; background: linear-gradient( 30deg, rgba(229, 23, 49, 1), rgba(229, 23, 49, .9) 48%, transparent 55%, ); filter: url(#smoke); } }
改造后的效果如下:
好,又接近了一步,但是四周有很多瑕疵没有被填满。问题不大,我们改变一下定位的 top \ left\ right \ bottom,让伪元素超出父容器,父容器设置 overflow: hidden 即可:
div { .... overflow: hidden; &::before { .... left: -20px; top: -10px; right: -20px; bottom: -20px; background: linear-gradient( 30deg, rgba(229, 23, 49, 1), rgba(229, 23, 49, .9) 48%, transparent 55%, ); filter: url(#smoke); } }
调整之后,看看效果:
有点那感觉了,下一步,只需要让烟雾元素动起来,为了让整个效果连贯(由于 SVG 动画本身不支持类似 animation-fill-mode: alternate 这种特性),我们还是需要写一点 JavaScript 代码,控制动画的整体循环。
大概的代码是这样:
const filter = document.querySelector("#turbulence"); let frames = 1; let rad = Math.PI / 180; let bfx, bfy; function freqAnimation() { frames += .35; bfx = 0.035; bfy = 0.015; bfx += 0.006 * Math.cos(frames * rad); bfy += 0.004 * Math.sin(frames * rad); bf = bfx.toString() + " " + bfy.toString(); filter.setAttributeNS(null, "baseFrequency", bf); window.requestAnimationFrame(freqAnimation); } window.requestAnimationFrame(freqAnimation);
这段代码做的事情,其实只有一个,就是让 SVG 的 #turbulence
滤镜的 base
Frequency 属性,在一个区间内无限循环,仅此而已。通过改变 baseFrequency,让整个烟雾不断变化。
至此,我们就得到了一幅完整的,会动的烟雾遮罩:
补充下框内的图片,就能得到一开始给出的效果图效果:
完整的代码,你可以戳这里 -- CodePen Demos -- LPL BAN PICK MASK Effect
实现呼吸状态的遮罩效果
在上述基础上,再加入呼吸的效果,其实就非常简单了。
我们只需要去改变渐变的一个位置即可,方法非常多,这里我给一个较为优雅但是兼容性可能没那么好的方法 -- CSS @property。
简单改造上述代码:
@property --per { syntax: "<percentage>"; inherits: false; initial-value: 22%; } div::before { ... background: linear-gradient( 30deg, #ff0020, rgba(229, 23, 49, .9) var(--per), transparent calc(var(--per) + 8%), ); filter: url(#smoke); animation: change 2s infinite ease-out; } @keyframes change { 50% { --per: 18%; } }
这样,呼吸效果就实现了:
完整的代码,你可以戳这里 -- CodePen Demos -- LPL BAN PICK MASK Effect