javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > JS requestAnimationFrame和setTimeout

JS中requestAnimationFrame和setTimeout有什么不同

作者:Hyyy

本文主要介绍了JS中requestAnimationFrame和setTimeout有什么不同,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在学习 requestAnimationFrame 之前,需要先建立两个底层认知,理解动画流畅性的本质与传统方案的缺陷。

1. 显示器刷新率与浏览器的 “帧”

2. 传统定时器动画的核心痛点

早期前端动画普遍通过 setTimeoutsetInterval 定时修改元素样式实现,存在两个原生缺陷:

requestAnimationFrame 正是为解决动画的时机同步与性能优化问题而诞生的原生 API。

一、requestAnimationFrame 核心概念

1. 定义

requestAnimationFrame(行业内常简称 rAF)是 HTML5 标准定义的浏览器原生 API,用于向浏览器注册一个回调函数,要求浏览器在下一次屏幕重绘之前执行该回调,是专门面向视觉动画优化的调度接口。通俗解释:它相当于给浏览器发送一个 “渲染前执行” 的预约,让动画代码和屏幕的刷新节奏天然对齐,从机制上避免不同步导致的卡顿。

2. 底层原理

rAF 的核心本质是与浏览器渲染流水线深度绑定的回调调度机制,核心逻辑有三点:

  1. 刷新率自动对齐:浏览器会根据当前显示器的刷新率,自动调度回调的执行频率,保证每帧最多执行一次回调,不会出现多余的计算。
  2. 精准执行时机:回调固定在每帧的「JS 任务执行完毕、样式计算开始之前」执行,此时修改元素样式,会在当前帧完成布局与绘制,不会出现跨帧延迟。
  3. 后台自动休眠:当页面标签页被隐藏、最小化时,浏览器会自动暂停 rAF 回调的调度;页面重新可见时自动恢复,以此降低性能消耗。

3. 具象示例

下面是用 rAF 实现元素水平匀速移动的标准写法,也是行业通用的最佳实践:

const target = document.querySelector('.animate-box');
const totalDistance = 300; // 总移动距离 300px
const totalDuration = 2000; // 动画总时长 2000ms

let startTimestamp = null; // 动画起始时间戳

// 每帧执行的动画函数
function step(currentTimestamp) {
  // 首次执行时记录起始时间
  if (!startTimestamp) startTimestamp = currentTimestamp;
  
  // 计算已运行时长与动画进度
  const elapsed = currentTimestamp - startTimestamp;
  const progress = Math.min(elapsed / totalDuration, 1); // 进度限制在 0~1
  
  // 更新元素位置
  target.style.transform = `translateX(${progress * totalDistance}px)`;
  
  // 进度未完成则继续注册下一帧回调
  if (progress < 1) {
    requestAnimationFrame(step);
  }
}

// 启动动画
requestAnimationFrame(step);

该写法的核心特点是:通过时间差计算动画进度,而非固定每帧移动固定像素。即使设备帧率波动,动画总时长也保持一致,不会出现慢设备上动画变慢的问题。

4. 应用场景

二、requestAnimationFrame 的执行时序

1. 定义

执行时序指 rAF 回调在浏览器单帧渲染流水线中所处的具体阶段,以及它和其他异步任务的先后执行顺序。

2. 底层原理

浏览器完成一帧画面的标准流水线,按执行顺序分为以下阶段:

  1. 处理用户输入事件(点击、滚动、键盘输入等)
  2. 执行宏任务与微任务(即 JavaScript 事件循环中的任务队列)
  3. 执行 requestAnimationFrame 回调队列
  4. 样式计算(Style):计算所有元素最终生效的 CSS 样式
  5. 布局(Layout,俗称回流):计算元素的位置与尺寸
  6. 绘制(Paint,俗称重绘):将元素的视觉内容绘制到对应图层
  7. 合成(Composite):将多个图层合并为最终画面,输出到显示器

rAF 回调恰好位于「JS 常规任务之后、样式计算之前」,这个时机修改样式,会被当前帧的布局、绘制流程直接处理,是修改视觉样式的最佳时机。

3. 具象示例

对比定时器与 rAF 修改样式的生效时机差异:

// 写法 A:setTimeout 修改样式
setTimeout(() => {
  target.style.width = '200px';
  // 该修改大概率错过当前帧,需要等到下一帧才会渲染到屏幕
}, 0);

// 写法 B:rAF 修改样式
requestAnimationFrame(() => {
  target.style.width = '200px';
  // 该修改会在当前帧的布局阶段被计算,同帧即可渲染生效
});

setTimeout(fn, 0) 会将回调放入下一轮宏任务队列,执行时机晚于当前帧的渲染流程;而 rAF 回调在当前帧渲染前执行,样式修改可以立刻被本次渲染流水线处理。

4. 应用场景

三、与传统定时器动画的选型对比

1. 定义

setTimeout / setInterval 是通用型定时任务 API,设计目标是延迟执行任务;requestAnimationFrame 是专属渲染优化的 API,设计目标是流畅的视觉更新。两者底层调度机制完全不同,适用场景有明确边界。

2. 底层原理

3. 具象对比

表格

对比维度setTimeout / setIntervalrequestAnimationFrame
执行时机事件循环调度,与渲染周期无关绑定渲染周期,每帧重绘前固定执行
帧率稳定性不稳定,易掉帧、跳帧稳定,自动对齐显示器刷新率
后台页面行为持续执行,占用系统资源自动暂停,页面恢复后自动继续
时间精度毫秒级,存在最小 4ms 延迟限制微秒级高精度时间戳,无额外延迟
回调堆积高频调用易产生回调堆积单帧内多次调用合并执行,无堆积
设计定位通用异步定时任务专门面向视觉动画与渲染优化

4. 应用场景

四、requestAnimationFrame 的核心特性

1. 定义

rAF 自带三项原生特性,是其成为动画首选方案的核心优势:后台自动休眠、高精度时间戳、可取消机制。

2. 底层原理

3. 具象示例

标准的动画启停封装写法:

class AnimationController {
  constructor() {
    this.animationId = null;
  }

  start(animateFn) {
    const step = (timestamp) => {
      animateFn(timestamp);
      this.animationId = requestAnimationFrame(step);
    };
    this.animationId = requestAnimationFrame(step);
  }

  stop() {
    if (this.animationId) {
      cancelAnimationFrame(this.animationId);
      this.animationId = null;
    }
  }
}

在组件卸载、弹窗关闭等场景下调用 stop() 方法,可以彻底清理回调,避免内存泄漏与无效执行。

4. 应用场景

五、常见认知误区

  1. 误区:requestAnimationFrame 一定是 16.6ms 执行一次

    纠正:执行间隔由显示器刷新率决定。60Hz 屏幕约 16.6ms,120Hz 高刷屏约 8.3ms;若主线程被长任务阻塞,帧间隔会进一步拉长,rAF 会自动推迟执行,并非固定时间间隔。

  2. 误区:用了 requestAnimationFrame 动画就一定流畅

    纠正:rAF 只解决了执行时机同步的问题。如果回调内部包含大量计算、频繁触发重排重绘,总工作量超过单帧时间预算,动画依然会卡顿。流畅动画的核心是控制单帧内的总工作量,而非单纯使用 rAF。

  3. 误区:requestAnimationFrame 只能用来做动画

    纠正:rAF 的本质是「下一帧渲染前执行」,除动画外,还可用于大批量 DOM 分片渲染、避免强制同步布局、拆分长任务等场景,所有需要和渲染节奏对齐的逻辑都可以使用。

  4. 误区:requestAnimationFrame 和 setTimeout (fn, 0) 效果相近

    纠正:两者执行时机完全不在同一层级。setTimeout(fn, 0) 是将回调放入下一轮宏任务队列,执行时机晚于当前帧渲染;rAF 回调在当前帧渲染前执行,时机远早于 setTimeout 0,且精度更高。

六、落地实操建议

基础使用规范

性能优化建议

边界兼容建议

到此这篇关于JS中requestAnimationFrame和setTimeout有什么不同的文章就介绍到这了,更多相关JS requestAnimationFrame和setTimeout内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文