HTML5 中的requestAnimationFrame、requestIdCallback及二者的区别

浏览器运行机制

通过上图可看到,一帧内需要完成如下六个步骤的任务:

  • 处理用户的交互
  • JS 解析执行
  • 帧开始。窗口尺寸变更,页面滚去等的处理
  • requestAnimationFrame(rAF)
  • 布局
  • 绘制

requestAnimationFrame

通常js动画是利用setTimeout和setInterval,问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行。

setTimeoutsetInterval 是使用定时器来触发回调函数的,而定时器并无法保证能够准确无误的执行,有许多因素会影响它的运行时机,比如说:当有同步代码执行时,会先等同步代码执行完毕,异步队列中没有其他任务,才会轮到自己执行

由于大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms。

requestAnimationFrame采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果

requestAnimationFrame的运行机制:(在每次渲染前执行,每一帧都会执行)

1,合并每一帧的dom操作,在一次重绘和回流中更新;

2,不操作隐藏元素

3,页面未激活状态下,动画暂停

语法:
 

requestID = requestAnimationFrame(callback); 

cancelAnimationFrame方法用于取消定时器

兼容性:

if(!window.requestAnimationFrame){
    var lastTime = 0;
    window.requestAnimationFrame = function(callback){
        var currTime = new Date().getTime();
        var timeToCall = Math.max(0,16.7-(currTime - lastTime));
        var id  = window.setTimeout(function(){
            callback(currTime + timeToCall);
        },timeToCall);
        lastTime = currTime + timeToCall;
        return id;
    }
}

实例:

<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(){
    myDiv.style.width = '0';
    cancelAnimationFrame(timer);
    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 + '%';
            timer = requestAnimationFrame(fn);
        }else{
            cancelAnimationFrame(timer);
        }    
    });
}
</script>

requestIdCallback

requestIdCallback能够在主事件循环上执行后台和低优先级任务,而不会影响延迟关键事件,如动画输入响应

和requestAnimationFrame不同,在每一帧有空余时间时(正常帧任务没有超过16ms),执行requestIdCallback里面注册的任务

怎么确保requestIdCallback执行

假如浏览器一直处于非常忙碌的状态,requestIdleCallback 注册的任务有可能永远不会执行。

此时可通过设置 timeout来保证执行。

使用:

let handle = window.requestIdleCallback(callback[, options])
  • callback:回调,即空闲时需要执行的任务,该回调函数接收一个IdleDeadline对象作为入参。其中IdleDeadline对象包含:
    • didTimeout,布尔值,表示任务是否超时,结合 timeRemaining 使用。
    • timeRemaining(),表示当前帧剩余的时间,也可理解为留给任务的时间还有多少。
  • options:目前 options 只有一个参数
    • timeout。表示超过这个时间后,如果任务还没执行,则强制执行,不必等待空闲。


实例:

requestIdleCallback(myNonEssentialWork, { timeout: 2000 });
​
// 任务队列
const tasks = [
 () => {
   console.log("第一个任务");
 },
 () => {
   console.log("第二个任务");
 },
 () => {
   console.log("第三个任务");
 },
];
​
function myNonEssentialWork (deadline) {
 // 如果帧内有富余的时间,或者超时
 while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && tasks.length > 0) {
   work();
 }
​
 if (tasks.length > 0)
   requestIdleCallback(myNonEssentialWork);
 }
​
function work () {
 tasks.shift()();
 console.log('执行任务');
}

cancelIdleCallback

与 setTimeout 类似,返回一个唯一 id,可通过 cancelIdleCallback 来取消任务。

二者的区别:

requestAnimationFrame属于高级任务,每一帧都会执行,

requestIdCallback属于低级任务,每一帧不一定执行

说明:

因为requestIdCallback发生在一帧的最后,此时页面布局已经完成,所以不建议在 requestIdleCallback 里再操作 DOM,这样会导致页面再次重绘。DOM 操作建议在 rAF 中进行。同时,操作 DOM 所需要的耗时是不确定的,因为会导致重新计算布局和视图的绘制,所以这类操作不具备可预测性。

Promise 也不建议在这里面进行,因为 Promise 的回调属性 Event loop 中优先级较高的一种微任务,会在 requestIdleCallback 结束时立即执行,不管此时是否还有富余的时间,这样有很大可能会让一帧超过 16 ms。


 

参考:https://www.cnblogs.com/xiaohuochai/p/5777186.html

https://blog.csdn.net/whqet/article/details/42911059

https://www.jianshu.com/p/2771cb695c81

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值