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

使用requestAnimationFrame与Date.now()时出现delta值不一致问题

问题:requestAnimationFrame 传入的 timestamp 与 Date.now() 计算的时间间隔差异异常

我编写了一段简单代码:

let startTime = -1
let lastNow = -1
let lastTS = -1
const animate = (timeStamp)=>{
  requestAnimationFrame(animate)
  const now = Date.now()
  if(lastTS===-1){
    lastTS = timeStamp
    lastDate = now
  }
  const deltaTS = timeStamp - lastTS
  lastTS = timeStamp
  const deltaNow = now - lastNow
  lastNow = now
  console.log(deltaTS,deltaNow)
}

animate(-1)

单独运行时,deltaTS与deltaNow数值相近,timestamp精度更高。但放入大型代码库后,结果差异极大:通过requestAnimationFrame的timestamp计算的deltaTS与刷新率一致,而Date.now()计算的deltaNow波动范围从4到很大的数。我怀疑animate可能被多次触发,但仍不理解为何Date.now()的时间间隔如此异常,求问可能的原因。


原因分析
  • 动画回调被多次注册,队列堆积导致状态混乱
    你怀疑animate被多次触发的方向是对的。如果代码库中重复调用animate(-1),会注册多个requestAnimationFrame回调。这些回调会在同一帧内依次执行:第一个回调把lastNow更新为当前时间,后续回调计算deltaNow时,就会得到极小值;如果主线程被阻塞导致帧推迟,下一批回调执行时deltaNow又会出现超大值。而requestAnimationFrame的timestamp是每帧统一生成的固定值,所以不管同一帧有多少个回调,deltaTS都会和刷新率匹配。

  • 主线程被长时间阻塞
    大型代码库中常存在复杂DOM操作、大数据计算、同步请求等耗时任务,这些任务会阻塞主线程,导致requestAnimationFrame回调被推迟执行。Date.now()取的是当前系统时间,两次回调的实际间隔会因阻塞被拉长,deltaNow自然波动大;而requestAnimationFrame的timestamp是浏览器在帧调度节点生成的时间,即使回调被推迟,依然基于原本的帧周期计算,所以deltaTS保持稳定。

  • 全局变量被意外篡改
    你代码中的lastNow、lastTS都是全局变量,大型代码库中很容易出现变量名冲突,或者其他逻辑意外修改了这些变量的值。比如某个地方重置了lastNow,下一次计算时就会得到异常的deltaNow值。

  • 系统时间波动影响Date.now()
    Date.now()依赖系统时间,若系统在运行时进行NTP校时、时间调整,都会导致其返回值出现跳变;而requestAnimationFrame的timestamp基于浏览器内部的高精度单调计时器,不受系统时间调整影响,所以始终保持稳定的帧间隔。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 13:05:23