You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让requestAnimationFrame动画按照指定时长准确运行

requestAnimationFrame相关问题解答

1. 为什么帧间隔不恒定、前几帧耗时偏高?

requestAnimationFrame(以下简称rAF)的回调执行时机和浏览器的渲染流水线完全绑定,只要浏览器主线程有其他优先级更高的任务在执行(比如同步JS运算、大规模DOM更新、用户交互事件处理),rAF回调就会被延后,自然不会保持固定间隔。
而前几帧耗时偏高是常见现象,动画启动初期浏览器通常需要完成额外的初始化工作,比如样式重新计算、渲染图层分配、缓存预热等,都会占用额外时间。

2. 16.5ms的间隔是否会随设备/屏幕变化?

会,16.5ms只是60Hz刷新率屏幕的理论刷新间隔(1000ms / 60 ≈ 16.67ms),这个值会随屏幕刷新率变化:120Hz屏的刷新间隔约为8.3ms,144Hz屏约为6.9ms,部分设备开启省电模式时还会主动降低刷新率,间隔会进一步变长,完全不能用固定的16.5ms作为计算基准。

3. 如何保证动画刚好在指定时长内完成?

不要用固定步长累加的方式计算位移,正确做法是用时间差计算动画进度,完全和帧间隔解耦,实现逻辑如下:

  • 动画启动时记录起始时间戳
  • 每帧rAF触发时用传入的timestamp计算已运行时长,再除以总时长得到当前动画进度(最大为1)
  • 用进度乘以总位移高度得到当前需要移动的距离,再更新样式即可

修改后的代码示例:

function changeHeight(children, duration, setTop) {
  const height = children.clientHeight;
  // 记录动画起始时间
  let startTime = null;

  const slideUp = function (timestamp) {
    // 第一帧触发时初始化起始时间
    if (!startTime) startTime = timestamp;
    // 计算已运行时长
    const elapsed = timestamp - startTime;
    // 计算当前进度,最大不超过1
    const progress = Math.min(elapsed / duration, 1);
    // 计算当前需要移动的距离
    const currentMove = progress * height;

    children.style.top = `${setTop(currentMove, height)}px`;

    // 进度没到1就继续下一帧
    if (progress < 1) {
      requestAnimationFrame(slideUp);
    }
  };

  requestAnimationFrame(slideUp);
}

这个实现完全不依赖固定帧间隔,不管是高刷屏还是卡慢设备,都能保证动画刚好在你传入的duration时长内完成。


内容的提问来源于stack exchange,提问作者McDerp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 20:24:04