早期的定时动画
以 setInterval() 来控制动画执行
;(function(){
function updateAnimations(){
doAnimation1();
doAnimation2();
// 其它任务
}
setInterval(updateAnimations, 100);
})();
作为一个小型动画库的标配,这个 updateAnimations() 方法会周期性运行注册的动画任务,并反映出每个任务的变化。如果没有动画需要更新,则这个方法即可以什么不做直接退出,也可以停止动画循环,等待其它需要更新的动
缺点
问题在于无法准确知晓循环之间的延时。
- 定时间隔必须足够短,才能让不同的动画类型都能平滑顺畅,但又要足够长,以便浏览器可以渲染出来的变化。
- 实现平滑动画最佳重绘间隔为1000毫秒 / 60,大约 17 毫秒。
不能保证时间精度
- 第二个参数的延时只能保证何时会把代码添加动任务队列,但不能保证添加到队列就会立即执行。
参数
- 重绘屏幕前调用的函数
点击查看【codepen】
requestAnimationFrame() 只会调用一次传入的函数,每次更新用户界面时需要再手动调用它一次。
传给 requestAnimationFrame() 的函数可以接收一个参数
- DOMHighResTimeStamp 实例,performance.now() 表示下次重绘的时间
- 实际上把重绘任务安排在未来一个书籍的时间点上
cancelAnimationFrame
类似于 clearInterval()
通过 requestAnimationFrame() 返回的请求 ID,以 cancelAnimationFrame 取消重绘任务。
通过 requestAnimationFrame 节流
这个方法实际上会在浏览器执行下一次重绘之前的一个点,调用函数。每次调用 requestAnimationFrame() 都会在生重绘队列上推入一个回调函数,队列的长度没有限制。
定义一个标志变量,由回调设置其开关状态,就可以将多余的调用屏蔽
let enqueued = false;
function expensiveOperation() {
console.log('Invoked at', Date.now());
enqueued = false;
}
window.addEventListener('scroll', () => {
if (!enqueued) {
enqueued = true;
window.requestAnimationFrame(expensiveOperation);
}
});
因为重绘是非常频繁的操作,所以这还算不上真正的节流。更好的办法是配合使用一个计时器来限制操作执行的频率。这样,计时器可以限制实际的操作执行间隔,而 requestAnimationFrame 控制在浏览器的哪个渲染周期中执行。下面的例子可以将回调限制为不超过 50 毫秒执行一次
let enabled = true;
function expensiveOperation() {
console.log('Invoked at', Date.now());
}
window.addEventListener('scroll', () => {
if (enabled) {
enabled = false;
window.requestAnimationFrame(expensiveOperation);
window.setTimeout(() => enabled = true, 50);
}
});