前端动画的利器:`requestAnimationFrame` 与定时器的全面对决

68 阅读7分钟

☕ 404 星球 · 前端晨间广播 | 2025-10-29

1️⃣ React 19 稳定版发布,并发特性默认开启

  • • 全新 use() Hook 支持在组件顶层读取 Promise & Context
  • • 自动批处理范围扩大至 setTimeout / 事件处理器,零配置性能提升 8~12%
  • • 官方兼容检查工具 react-compat-19 一键扫描废弃 API,CI 友好

2️⃣ TypeScript 5.7 带来“类型收窄新魔法”

  • satisfies 运算符增强:可在泛型内部直接约束字面量类型,减少 30% 冗余代码
  • • 性能优化:增量编译缓存命中率提升,大型单体仓库编译时间再降 18%
  • • 即刻通过 npm i typescript@rc 尝鲜,正式版 11 月中旬落地

3️⃣ ECMAScript 2026 草案:管道运算符 |> 进入 Stage-3

  • • 写法:value |> fn1 |> fn2;Babel / SWC 已同步实现
  • • 配合部分应用语法 foo(?),可将嵌套回调改写为“链式”平面结构,降低心智负担
  • • Chrome 136 与 Firefox 128 预计 2026 Q1 首发货真价实原生支持

4️⃣ Vite 6 RC 2 发布,默认启用 Rolldown 打包核心

  • • 冷启动速度再砍 35%,大型项目 (>5k 模块) HMR 稳定在 80 ms 内
  • • 支持“增量产物签名”,实现多实例部署秒级回滚
  • • 配置项 100% 向下兼容,官方提供 vite-5to6 一键迁移 CLI

5️⃣ CSS @layer 现在可以“动态命名”

  • • Chrome 135 开启 Origin Trial:允许 layer-name: var(--foo) 运行时切换
  • • 与 prefers-color-scheme 联动,可一句代码完成“亮色/暗色”样式层重排
  • • 预计 2026 Q3 进入稳定通道,设计系统同学可以提前上车

— ☕️🌅 —

一、前言

在前端开发领域,实现流畅的动画效果对于提升用户体验至关重要。然而,动画的实现方式多种多样,如何选择合适的动画方法,是摆在开发者面前的一个重要问题。requestAnimationFrame 和定时器(setTimeoutsetInterval)都是常用的动画实现手段,但它们在原理、性能、适用场景等方面存在显著差异。本文将深入剖析 requestAnimationFrame 与定时器的对比,帮助开发者做出更明智的选择。

二、requestAnimationFrame 简介

基本概念

requestAnimationFrame 是浏览器提供的一个专门用于动画的 API,它告诉浏览器在下次重绘之前执行回调函数,以更新动画。其基本语法如下:


    
    
    
  let requestId = requestAnimationFrame(callback);
  • callback:一个函数,浏览器在下次重绘之前会调用该函数。该函数接收一个参数,表示当前时间(以毫秒为单位)。
  • requestId:一个整数,唯一标识了该次请求,可用于取消动画。

工作原理

浏览器在运行时,会维护一个事件循环,处理各种任务,如 JavaScript 执行、DOM 更新、页面重绘等。requestAnimationFrame 的回调函数会被放入浏览器的动画帧队列中,在事件循环的重绘阶段执行。具体来说,浏览器在每次重绘前,会检查动画帧队列,依次执行其中的回调函数,然后执行重绘操作,更新页面内容。

优点

    1. 与浏览器重绘同步requestAnimationFrame 的回调函数在浏览器重绘之前执行,能够确保动画的更新与页面重绘同步,避免不必要的重绘,提高动画性能。
    1. 优化的性能表现:浏览器会对 requestAnimationFrame 进行优化,当页面不可见或处于后台时,浏览器会暂停动画,节省资源消耗。
    1. 流畅的动画效果:由于其与浏览器重绘同步,动画的帧率能够与浏览器的刷新率相匹配,通常为 60Hz,从而实现流畅的动画效果。

局限性

    1. 兼容性问题:在一些老版本的浏览器中,requestAnimationFrame 可能不被支持,需要进行兼容性处理。
    1. 无法控制执行频率requestAnimationFrame 的回调函数执行频率由浏览器决定,无法像定时器那样精确控制执行间隔。

三、定时器简介

基本概念

定时器主要通过 setTimeoutsetInterval 两个函数实现,它们可以设置在特定的时间间隔后执行代码。

  • setTimeout:设置一个定时器,在指定的延迟后执行一次代码。

    
        
        
        
      let timeoutId = setTimeout(function, delay);
    
  • setInterval:设置一个定时器,按照指定的时间间隔周期性地执行代码。

    
        
        
        
      let intervalId = setInterval(function, delay);
    

工作原理

定时器会在设定的延迟时间后将回调函数放入 JavaScript 执行队列中,等待事件循环处理。然而,由于 JavaScript 的单线程特性,如果当前执行栈中有其他任务在执行,定时器的回调函数可能需要等待更长时间才能执行。

优点

    1. 简单易用:定时器的使用方式简单直观,易于理解和实现。
    1. 控制执行频率:可以精确设置执行间隔,适用于一些需要固定频率执行的场景。

局限性

    1. 性能问题:如果定时器的回调函数执行时间过长,会阻塞后续代码的执行,影响页面的响应性能。
    1. 无法保证执行间隔:由于 JavaScript 的单线程特性,定时器的实际执行间隔可能会比设定的延迟时间长,导致动画不流畅。
    1. 缺乏优化:浏览器不会对定时器进行特殊优化,即使页面不可见,定时器仍会继续执行,造成资源浪费。

四、requestAnimationFrame 与定时器的对比

性能对比

  • requestAnimationFrame:与浏览器重绘同步,性能更优,能够实现流畅的动画效果,且在页面不可见时会暂停,节省资源。
  • 定时器:可能因 JavaScript 执行队列的阻塞导致实际执行间隔不准确,影响动画性能,且无法像 requestAnimationFrame 那样进行优化。

适用场景对比

  • requestAnimationFrame:适用于大多数动画场景,尤其是需要流畅动画效果的交互式动画,如游戏、复杂动画等。
  • 定时器:适用于一些简单的、对动画流畅度要求不高的场景,或者需要精确控制执行频率的非动画任务,如定期发送请求、倒计时等。

代码示例对比

使用 requestAnimationFrame 实现动画


    
    
    
  let start = null;
const element = document.getElementById('animated-element');

function animate(timestamp) {
  if (!start) start = timestamp;
  const elapsed = timestamp - start;
  
  // 假设动画持续2秒
  const progress = Math.min(elapsed / 20001);
  
  element.style.transform = `translateX(${progress * 100}px)`;
  
  if (progress < 1) {
    requestAnimationFrame(animate);
  }
}

requestAnimationFrame(animate);

使用 setInterval 实现动画


    
    
    
  let start = null;
const element = document.getElementById('animated-element');
const duration = 2000; // 动画持续时间
const startTime = Date.now();

const intervalId = setInterval(() => {
  const elapsed = Date.now() - startTime;
  const progress = Math.min(elapsed / duration, 1);
  
  element.style.transform = `translateX(${progress * 100}px)`;
  
  if (progress >= 1) {
    clearInterval(intervalId);
  }
}, 16); // 约60帧每秒

在上述示例中,使用 requestAnimationFrame 的动画更加流畅,且浏览器会进行优化,而使用 setInterval 的动画可能因各种因素出现卡顿。

五、最佳实践建议

    1. 优先使用 requestAnimationFrame:在实现动画时,应优先考虑使用 requestAnimationFrame,以获得更好的性能和流畅度。
    1. 合理处理兼容性:对于不支持 requestAnimationFrame 的浏览器,可以使用 setTimeout 进行降级处理,但要确保动画的流畅性。
    1. 避免在动画回调中执行复杂操作:无论是使用 requestAnimationFrame 还是定时器,都应避免在回调函数中执行复杂的计算或 DOM 操作,以免影响动画性能。
    1. 使用 cancelAnimationFrame 取消动画:当动画不再需要时,应及时使用 cancelAnimationFrame 取消动画,释放资源。

六、结语

requestAnimationFrame 和定时器各有优缺点,在动画实现中,requestAnimationFrame 通常能提供更好的性能和流畅度,是现代前端动画的首选方法。然而,定时器在一些特定场景下仍有其应用价值。开发者应根据具体需求和场景,合理选择动画实现方式,以达到最佳的用户体验。通过深入理解两者的差异,我们能够更加高效地开发出高性能、高质量的动画效果,为用户带来更加出色的视觉体验。