出处:掘金
原作者:某某某人
rAF 是什么
window.requestAnimationFrame()
告诉浏览器 —— 希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
rAF 是浏览器提供的和渲染帧率保持同步的一个 API,注意是渲染帧率不是屏幕刷新率
例子:
// 获取需要动画处理的元素
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 的执行时机:
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)';
});
});
运行效果
Chrome 124.0.6367.62
Safari 14.0.3
Firefox 125.0.2
结论
- Chrome 和 Safari 一样,会被推迟到下一帧的渲染流程前去执行。貌似和规范相比不一致
- Firefox 的表现和规范的描述是一致的
看起来浏览器的实现和 规范 并不完全一致,rAF 运行时机可以归纳为在 JS 执行后,在当前帧或下一帧渲染流程前,具体和浏览器有关
可以实现一个 nextFrameExecute
函数,确定使其运行在下一帧:
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/dynamic-tasks
rAF 属于宏任务还是微任务?
首先这个问题就不应该这样问,它实际上属于浏览器的渲染机制的一部分,并拥有自己的特定时机和队列。它既不是宏任务也不是微任务
非要归类到这两个中,那么 rAF 的执行时机和表现特性类似于宏任务
使用场景
JS 运行时性能优化
可以将一个长任务拆分为 N 多个小的任务,这些操作将在浏览器的每一帧中执行。通过这样做,可以避免长时间执行脚本造成的主线程阻塞,从而防止界面冻结和用户交互延迟
详情参考:js性能优化:时间切片分帧,webworker并行, requestidlecallback空闲执行,延迟执行~~
与浏览器的渲染同步,优化动画的运行时性能
rAF 为更新动画提供了一个最佳时机——就在浏览器准备重绘之前。这可以确保在屏幕更新的每一帧中都使用最新的动画状态,避免动画的跳帧或不必要的重绘和重回、提高动画流畅性和视觉效果。这一点对于实现流畅的用户界面动效至关重要
节能省电
当用户切换到其他标签或最小化窗口时,浏览器可能不会执行 rAF 回调,从而减少 CPU 和 GPU 的使用,节省电能。这对于移动设备和电池供电的设备尤其有利
减少页面布局抖动
通过在适当的时间进行 DOM 更新,rAF 可以帮助减少由于频繁的样式和布局变更引发的抖动问题,这是因为所有的变更都是在浏览器绘制之前完成的