浏览器运行机制

通过上图可看到,一帧内需要完成如下六个步骤的任务:
- 处理用户的交互
- JS 解析执行
- 帧开始。窗口尺寸变更,页面滚去等的处理
- requestAnimationFrame(rAF)
- 布局
- 绘制
requestAnimationFrame
通常js动画是利用setTimeout和setInterval,问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行。
setTimeout 或 setInterval 是使用定时器来触发回调函数的,而定时器并无法保证能够准确无误的执行,有许多因素会影响它的运行时机,比如说:当有同步代码执行时,会先等同步代码执行完毕,异步队列中没有其他任务,才会轮到自己执行。
由于大多数电脑显示器的刷新频率是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
773

被折叠的 条评论
为什么被折叠?



