requestAnimationFrame和requestIdleCallback
requestAnimationFrame
requestAnimationFrame函数,如果你希望执行一个动画,并且要求浏览器在下次重绘前执行指定的回调函数来更新动画,该方法需要传一个回调函数作为参数,会在浏览器下次重绘前执行回调函数。 浏览器FPS通常为60, 即一秒刷新60次,requestAnimationFrame 每次调用间隔为16.7ms。 requestAnimationFrame只会调用一次传入的函数,所以每次更新用户界面时需要再手动调用它一次。 目前为止,requestAnimationFrame已经解决了浏览器不知道 JavaScript 动画何时开始的问题, 以及最佳间隔是多少的问题,但是,不知道自己的代码何时实际执行的问题呢?这个方案同样也给出了 解决方法。
传给 requestAnimationFrame()的函数实际上可以接收一个参数,此参数是一个 DOMHighRes- TimeStamp 的实例(比如 performance.now()返回的值),表示下次重绘的时间。这一点非常重要: requestAnimationFrame()实际上把重绘任务安排在了未来一个已知的时间点上,而且通过这个参数 告诉了开发者。基于这个参数,就可以更好地决定如何调优动画了。 示例:
const element = document.getElementById('some-element-you-want-to-animate');
let start;
function step(timestamp) {
if (start === undefined)
start = timestamp;
const elapsed = timestamp - start;
//这里使用`Math.min()`确保元素刚好停在200px的位置。
element.style.transform = 'translateX(' + Math.min(0.1 * elapsed, 200) + 'px)';
if (elapsed < 2000) { // 在两秒后停止动画
window.requestAnimationFrame(step);
}
}
window.requestAnimationFrame(step);
cancelAnimationFrame
与 setTimeout()类似,requestAnimationFrame()也返回一个请求 ID,可以用于通过另一个 方法 cancelAnimationFrame()来取消重绘任务。下面的例子展示了刚把一个任务加入队列又立即将 其取消:
let requestID = window.requestAnimationFrame(() => {
console.log('Repaint!');
window.cancelAnimationFrame(requestID);
})
通过requestAnimationFrame节流
通过 requestAnimationFrame()递归地向队列 中加入回调函数,可以保证每次重绘最多只调用一次回调函数。这是一个非常好的节流工具。在频繁执 行影响页面外观的代码时(比如滚动事件监听器),可以利用这个回调队列进行节流。 这样会把所有回调的执行集中在重绘钩子,但不会过滤掉每次重绘的多余调用。
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);
}
});
requestIdleCallback
requestIdleCallback 该函数在浏览器空闲的时间调用,可在事件循环上执行后台和优先级低的工作,而不会影响延迟关键事件,如页面的动画和输入响应等。函数一般按照先进先调用的顺序执行,如果指定了timeout, 则可能会为了在超时前执行函数而打乱执行顺序。
示例:
requestIdleCallback(myNonEssentialWork);
function myNonEssentialWork (deadline) {
// deadline.timeRemaining()可以获取到当前帧剩余时间
while (deadline.timeRemaining() > 0 && tasks.length > 0) {
doWorkIfNeeded();
}
if (tasks.length > 0){
requestIdleCallback(myNonEssentialWork);
}
}
requestIdleCallback 的fps 为20 不能满足现有的用户体验需求,react 中模拟实现了这一功能。
requestAnimationFrame和requestIdleCallback的区别
我么所看到的浏览器的动画效果,为一帧一帧绘制出来的,通常FPS为60的设备,流畅度就很顺畅了。浏览器在每次重绘更新之前,需要用户的交互,js的执行、reuestAnimationFrame的的调用和布局计算以及页面的重绘。
如果某一帧执行的内容不多,还有空余的时间,也就是不到16ms的时间,就有空余的时间来执行requestIdleCallback的回调。 如果浏览器一直处于空闲状态,没有任何交互等需要处理的内容,这时候留给requestIdleCallback回调的时间会相应长一些,最长可达到50ms,以防出现不可预测的任务如用户输入等来临时无法及时响应造成用户感知上的延迟和卡顿。
requestAnimationFrame会在浏览器渲染的每一帧确定前执行,属于高优先级任务,而requestIdleCallback的回调则不确定,属于低优先级任务。
使用场景
requestAnimationFrame的使用场景,如抽奖转盘动画等,比setTimeout、setInterval更流畅。 requestIdleCallback的使用场景,比如页面有输入框和很长列表的数据渲染,用户在输入过程中,输入的流程度优先级要高于列表的数据渲染,可以将列表的渲染放在不那么重要的去执行。
评论(4)
登录后参与评论。
- 游游客
1
- 游游客
1
- 游游客
2
- 游游客
312

