乐趣区

由奥迪车灯想到的前端动画

最近对汽车比较感兴趣,平时也多留意看了一些身边的车,发现奥迪部分车型的转向灯很有特色,有一个从左到右的动画效果,视觉效果很赞,这撩起了我的好奇心,怎么用代码在网页上模拟实现呢?
先来看看我们需要实现的效果:预览
方法一:css
首先,想到的自然是 css,因为现在 css 动画功能很强大,且这种方式代码简单,效果顺滑,强烈推荐。代码如下:
<div class=”css-light”></div>
div.css-light {
width: 0%;
height: 6px;
background: #ff8908;
animation: mymove 1s ease-out infinite;
}
@keyframes mymove {
from {
width: 0%;
}
to {
width: 100%;
}
}
其中,核心属性是 animation。那 animation 怎么用呢?如下:第一个属性指定要绑定到选择器的关键帧的名称,本例是 mymove;第二个属性指定动画指定需要多少秒或毫秒完成,本例是 1s;第三个属性设置动画将如何完成一个周期,默认是 ease,本例是 ease-out,表示动画以低速结束;第四个属性定义动画的播放次数,本例是无限循环;
由此可见,css 的 animation 是真的强大,还支持多种动画效果,比如 ease-out 这种效果,但你要用 js 模拟,那可就不是一行代码的事儿了。
更多参数信息请参考:CSS3 animation(动画)属性 animation
方法二:js 方法
除开 css,js 也给与了模拟动画的能力,这种方式通常使用 setInterval() 方法来控制动画,代码如下:
<div id=”process”></div>
let div = document.getElementById(‘process’)

function process() {
div.style.width = ‘0%’

setInterval(() => {
div.style.width = (parseInt(div.style.width, 10) + 1) + ‘%’

if (div.style.width === ‘100%’) {
div.style.width = ‘0%’
}
}, 20)
}
process()
但这种方法有几个弊端。
第一个就是不十分精确,为它传入的第二个参数(如本例中的 20),实际上只是指定了把动画代码添加到浏览器 UI 线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行。也就是说,以毫秒表示的延迟时间并不代表到时候一定会执行动画代码,而仅代表到时候会把代码添加到任务队列中。如果 UI 线程繁忙,比如忙于处理用户操作,那么即使把代码加入队列也不会立即执行。第二个弊端就是编写动画循环需要知道延迟时间多长合适。一方面,循环间隔必须足够短,这样才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化。拿本例来说,如果将 20 改成 200,那么你会发现动画效果会显得一卡一卡的。而由 20 改成 1,那么动画会执行的非常快,并且性能的开销也会增加。
第三个弊端是 setInterval 它的开销也比较大,即便页面后台运行,这个开销也一直存在。
那么,这些个弊端能不能优化呢?这就要用到 requestAnimationFrame 了!与 setInterval 相比,requestAnimationFrame 最大的优势是由系统来决定回调函数的执行时机。具体来说,如果屏幕刷新率是 60Hz,那么回调函数就每 16.7ms 被执行一次,如果刷新率是 75Hz,那么这个时间间隔就变成了 1000/75=13.3ms,换句话说就是,requestAnimationFrame 的步伐跟着系统的刷新步伐走。它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题。省心吧,用了它,你就再也不用纠结到底循环间隔设置为多少合适了,它帮你干了这事。
它的用法很简单,用 requestAnimationFrame 方式改写如下:
function updateProgress () {
let div = document.getElementById(‘process’)

div.style.width = (parseInt(div.style.width, 10) + 1) + ‘%’
if (div.style.width === ‘100%’) {
div.style.width = ‘0%’
}
window.requestAnimationFrame(updateProgress);
}
window.requestAnimationFrame(updateProgress);
关于更多 requestAnimationFrame 的信息,请参考:深入理解 requestAnimationFramewindow.requestAnimationFrame
总结
本文模拟了一个简单的车灯动画效果,从中引出了一些前端动画方面的知识,并用二种不同的方式进行了实现(css 和 js)。最后说下,在汽车上真实的效果应该是由多个 led 灯来实现的,有兴趣的同学可以自行模拟和拓展。

退出移动版