浏览器: 深入理解requestAnimationFrame优化js运行时

先有问题再有答案

  1. rAF是什么?
  2. rAF是在当前帧执行还是在下一帧执行?
  3. rAF在一帧的执行时机是什么?
  4. rAF属于宏任务还是微任务?
  5. raf和js任务, 渲染流程什么关系?
  6. 如何利用rAF实现一个nextFrame的功能?
  7. 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 可以帮助减少由于频繁的样式和布局变更引发的抖动问题,这是因为所有的变更都是在浏览器绘制之前完成的。

参考

  1. requestanimationframe-scheduling-for-nerds
  2. react_issues
  3. mdn_requestAnimationFrame
  4. youtube
  5. requestAnimationFrame回调时机
  6. rAF
  7. html.spec
相关推荐
一 乐23 分钟前
智慧党建|党务学习|基于SprinBoot+vue的智慧党建学习平台(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·学习
BBB努力学习程序设计1 小时前
CSS Sprite技术:用“雪碧图”提升网站性能的魔法
前端·html
BBB努力学习程序设计1 小时前
CSS3渐变:用代码描绘色彩的流动之美
前端·html
冰暮流星1 小时前
css之动画
前端·css
jump6802 小时前
axios
前端
spionbo2 小时前
前端解构赋值避坑指南基础到高阶深度解析技巧
前端
用户4099322502122 小时前
Vue响应式声明的API差异、底层原理与常见陷阱你都搞懂了吗
前端·ai编程·trae
开发者小天2 小时前
React中的componentWillUnmount 使用
前端·javascript·vue.js·react.js
永远的个初学者3 小时前
图片优化 上传图片压缩 npm包支持vue(react)框架开源插件 支持在线与本地
前端·vue.js·react.js
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ3 小时前
npm i / npm install 卡死不动解决方法
前端·npm·node.js