参考:《JS高级程序设计》,这篇requestAnimationFrame文章
JS实现动画主要有三种方式:setTimeout
、setInterval
和 requestAnimationFrame
。其中最后一种方法比较好。
setTimeout
、setInterval
setTimeout
、setInterval
可以用来创建定时器。定时器对队列的工作方式是,当特定时间过去后将代码插入。
定时器指定的时间间隔表示何时将定时器的代码添加到队列,而不是何时实际执行代码!
setInterval
一个用setInterval
实现的进度条例子
<div id="myDiv" style="background-color: lightblue;width: 0;height: 20px;line-height: 20px;">0%</div>
<button id="btn">run</button>
<script>
var timer;
btn.onclick = function(){
// 取消对应的定时器,注意与clearTimeout不一样
clearInterval(timer);
// 注意是字符串,给parseInt用
myDiv.style.width = '0';
timer = setInterval(function(){
// parseInt解析一个字符串,返回一个整数
if(parseInt(myDiv.style.width) < 500){
myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
// 总长500,除以5的数字正好是百分比
myDiv.innerHTML = parseInt(myDiv.style.width)/5 + '%';
}else{
clearInterval(timer);
}
},16);
}
</script>
复制代码
setInterval
确保了定时器代码规则地插入队列中(注意不是规则的执行)。
当定时器代码要插入队列时,已经有一个定时器代码 正在运行,并且有一个 定时器实例 在等待,则此处不插入,跳过。 确保定时器代码加入到队列中的最小时间间隔为指定间隔(实际间隔有可能大于指定间隔)。
缺点:
- 某些间隔被跳过
- 多个定时器代码执行之间的间隔可能比预期的小
setTimeout
调用链式setTimeout
可以避免上述两个缺点,每次函数执行的时候都会创建换一个新的定时器。在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,确保不会有任何确实的间隔。并且确保在下一次定时器代码执行之前,至少要等待指定的间隔,避免了连续的运行。
一个用setTimeout
实现进度条的例子
<div id="myDiv" style="background-color: lightblue;width: 0;height: 20px;line-height: 20px;">0%</div>
<button id="btn">run</button>
<script>
var timer;
btn.onclick = function() {
// 注意与clearInterval不一样
clearTimeout(timer);
myDiv.style.width = '0';
// 给function取个名字后面链式用
timer = setTimeout(function fn(){
if(parseInt(myDiv.style.width) < 500) {
myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
myDiv.innerHTML = parseInt(myDiv.style.width)/5 + '%';
// 链式setTimeout
timer = setTimeout(fn,16);
} else {
clearTimeout(timer);
}
},16)
}
</script>
复制代码
requestAnimationFrame
用requestAnimationFrame
实现的进度条例子
<div id="myDiv" style="background-color: lightblue;width: 0;height: 20px;line-height: 20px;">0%</div>
<button id="btn">run</button>
<script>
var timer;
btn.onclick = function(){
// 注意cancelAnimationFrame
cancelAnimationFrame(timer);
myDiv.style.width = '0';
timer = requestAnimationFrame(function fn(){
if(parseInt(myDiv.style.width) < 500) {
myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
myDiv.innerHTML = parseInt(myDiv.style.width)/5 + '%';
// requestAnimationFrame只传入一个参数
timer = requestAnimationFrame(fn);
} else {
cancelAnimationFrame(timer);
}
}) // 只传入一个参数
}
</script>
复制代码
最大区别:采用系统时间间隔,最佳绘制。使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。
【1】requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率
【2】在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量
【3】requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销