先有问题再有答案
rAF是什么?
rAF是在当前帧执行还是在下一帧执行?
rAF在一帧的执行时机是什么?
rAF属于宏任务还是微任务?
raf和js任务, 渲染流程什么关系?
如何利用rAF实现一个nextFrame的功能?
requestAnimationFrame有什么用?
前置文章
浏览器:帧&事件循环
浏览器:帧&渲染流程
js性能优化:时间切片分帧,webworker并行, requestidlecallback空闲执行,延迟执行
js三座大山之异步一单线程,event loop,宏任务&微任务
rAF是什么
window.requestAnimationFrame()
告诉浏览器------你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
rAF是浏览器提供的和渲染帧率保持同步的一个api
.
注意是渲染帧率不是屏幕刷新率哦
两者的区别可以看这篇文章: js三座大山之异步五基于异步的js性能优化
例子:
javascript
// 获取需要动画处理的元素
const element = document.getElementById('animateMe');
// 初始化位置和速度
let position = 0;
const speed = 5; // 每帧移动5像素
function moveElement() {
// 更新元素的位置
position += speed;
element.style.left = `${position}px`;
// 如果元素还未移动到500px的位置,继续动画
if (position < 500) {
window.requestAnimationFrame(moveElement);
}
}
// 启动动画
window.requestAnimationFrame(moveElement);
调用时机
相信上面的图片 大家应该都不陌生了。
根据这个图片 我们的理解是raf
是在当前帧的js执行结束后,渲染流程前被调用的。
我也找了挺多资料的 里面提到了如下代码 可以验证raf
的执行时机。
ini
test.style.transform = 'translate(0, 0)';
document.querySelector('button').addEventListener('click', () => {
const test = document.querySelector('.test');
test.style.transform = 'translate(400px, 0)';
requestAnimationFrame(() => {
test.style.transition = 'transform 3s linear';
test.style.transform = 'translate(200px, 0)';
});
});
代码地址: safari-raf-bug.glitch.me/
运行效果:
chrome 版本124.0.6367.62(正式版本)
safari:版本14.0.3 看起来chrome和safari都是一样的 会被推迟到下一帧的渲染流程前去执行。 貌似和规范相比不一致
Firefox: 版本125.0.2 (64 位) Firefox的表现和规范的描述是一致的。
看起来浏览器的实现和 规范 并不完全一致,raf运行时机可以归纳为在js执行后,在当前帧或下一帧渲染流程前,具体和浏览器有关
。
我们可以实现一个nextFrameExecute函数
确定使其运行在下一帧, 类似下面这样的。
javascript
export const nextFrameExecute = async (task: Task) => {
let _resolve: Resolve;
let _reject: Resolve;
const p = new Promise((resolve, reject) => {
_resolve = resolve;
_reject = reject;
});
if (typeof requestAnimationFrame !== "undefined") {
// 浏览器兼容 确保推迟到下一帧
requestAnimationFrame(() => {
requestAnimationFrame(() => {
Promise.resolve(task()).then(_resolve, _reject);
})
});
return p;
}
setTimeout(() => {
Promise.resolve(task()).then(_resolve, _reject);
}, 16);
return p;
};
代码来自: github.com/wuyunqiang/...
rAF属于宏任务还是微任务?
首先这个问题就不应该这样问,它实际上属于浏览器的渲染机制的一部分,并拥有自己的特定时机和队列。它既不是宏任务也不是微任务。
非要归类到这两个中 那么rAF的执行时机和表现特性类似于宏任务。
作用
js运行时性能优化
我们可以将一个长任务拆分为N多个小的任务,这些操作将在浏览器的每一帧中执行。通过这样做,可以避免长时间执行脚本造成的主线程阻塞,从而防止界面冻结和用户交互延迟。
详情参考:js性能优化:时间切片分帧,webworker并行, requestidlecallback空闲执行,延迟执行~~
无优化的效果 运行长任务
优化后分帧运行
与浏览器的渲染同步,优化动画的运行时性能
rAF 为更新动画提供了一个最佳时机------就在浏览器准备重绘之前。这可以确保在屏幕更新的每一帧中都使用最新的动画状态,避免动画的跳帧或不必要的重绘(和重回)、提高动画流畅性和视觉效果。这一点对于实现流畅的用户界面动效至关重要。
节能省电
当用户切换到其他标签或最小化窗口时,浏览器可能不会执行 rAF 回调,从而减少 CPU 和 GPU 的使用,节省电能。这对于移动设备和电池供电的设备尤其有利。
减少页面布局抖动
通过在适当的时间进行 DOM 更新,rAF 可以帮助减少由于频繁的样式和布局变更引发的抖动问题,这是因为所有的变更都是在浏览器绘制之前完成的。