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

Chromium中多同域iframe的requestAnimationFrame回调频率异常升高求助

同域多iframe下requestAnimationFrame帧率异常问题

问题现象

我用TypeScript+WebGL开发视觉艺术作品,部分作品以iframe形式嵌入页面。发现当页面中嵌入多个同域作品iframe时,每个iframe的动画速度都会变快——经排查,每个独立iframe的requestAnimationFrame回调频率会随同级iframe数量增加而升高,并非整体帧率总和上升,而是单个iframe的运行速度异常加快。

复现情况:

  • 1个iframe时,帧率维持正常的约60Hz
  • 2个iframe时,每个iframe的FPS明显超过60(截图显示单iframe FPS突破100)
  • iframe数量越多,单iframe帧率上升越显著

额外差异:

  • 本地Vite开发服务器(npm run dev)嵌入作品时,帧率始终正常
  • 生产服务器同域作品嵌入时出现异常
  • 仅在Arch Linux系统的Chromium 126.0.6478.182中出现该问题,Firefox无异常

相关代码示例

嵌入多iframe的页面HTML

<body>
    <div id="stack-container">
        <div class="wrapper" style="opacity: 0.51; z-index: 0;"><iframe src="https://gfx.aimparency.org/controllable/fps/"></iframe></div>
        <div class="wrapper" style="opacity: 0; z-index: 1;"><iframe src="https://gfx.aimparency.org/controllable/fps/"></iframe></div>
    </div>
    <script type="module" src="/src/main.ts?t=1722986133016"></script>
    ...
</body>

iframe内的最小作品代码(TypeScript)

import './style.css'
import { Clock } from 'time-n-controls';

class Loop {
  clock = new Clock()
  fpsElement = document.getElementById('fps') as HTMLParagraphElement

  start() {
    this.loop() 
  }

  private loop() {
    this.clock.tick()
    this.fpsElement.innerText = this.clock.getTickSmoothFPS().toFixed(2)
    requestAnimationFrame(this.loop.bind(this))
  }
}

window.onload = () => {
  const loop = new Loop()
  loop.start()
}

Clock类核心代码

// tick shall be called once per frame, especially for tickDelta
private smoothFPS = 0
private previousTick = Date.now()
tick() {
  const now = Date.now()
  this.tickDeltaMS = now - this.previousTick
  this.tickDeltaS = this.tickDeltaMS * 0.001
  this.previousTick = now

  this.smoothFPS = this.smoothFPS * 0.9 + 0.1 / (this.tickDeltaS + 0.001)

  this.update()
  this.notifyQueues()
  this.updatedDynamicTimes(this.tickDeltaS)
}

getTickSmoothFPS() {
  return this.smoothFPS
}

原因猜测

怀疑Chromium对同域iframe的requestAnimationFrame调度存在异常:当多个同域iframe同时请求动画帧时,浏览器会为每个iframe触发所有iframe的回调,导致单个iframe的回调被高频重复调用。本地开发环境与生产环境的差异可能源于浏览器对本地服务器、生产服务器的同域策略或调度逻辑不同。

解决方案

1. 替换时间计算方式

Date.now()依赖系统时间,可能受同域iframe的时钟同步或浏览器节流影响,改用**高精度、单调递增的performance.now()**计算帧间隔,避免时间误差:

// 修改Clock类的tick方法
private smoothFPS = 0
private previousTick = performance.now()
tick() {
  const now = performance.now()
  this.tickDeltaMS = now - this.previousTick
  this.tickDeltaS = this.tickDeltaMS * 0.001
  this.previousTick = now

  this.smoothFPS = this.smoothFPS * 0.9 + 0.1 / (this.tickDeltaS + 0.001)

  this.update()
  this.notifyQueues()
  this.updatedDynamicTimes(this.tickDeltaS)
}

2. 强制限制帧间隔

手动设置每帧的最小间隔(60Hz对应约16.67ms),避免回调触发过于频繁:

// 修改Loop类的loop方法
private lastFrameTime = performance.now()
private loop() {
  const currentTime = performance.now()
  const delta = currentTime - this.lastFrameTime

  // 确保至少间隔16.67ms才处理下一帧
  if (delta >= 16.67) {
    this.clock.tick()
    this.fpsElement.innerText = this.clock.getTickSmoothFPS().toFixed(2)
    this.lastFrameTime = currentTime
  }

  requestAnimationFrame(this.loop.bind(this))
}

3. 父页面统一调度动画帧(同域场景)

由父页面创建唯一的requestAnimationFrame循环,通过消息通知所有iframe执行动画,避免重复触发:

  • 父页面代码:
const iframes = document.querySelectorAll('iframe')
function mainLoop() {
  iframes.forEach(iframe => {
    iframe.contentWindow.postMessage('tick', '*')
  })
  requestAnimationFrame(mainLoop)
}
requestAnimationFrame(mainLoop)
  • iframe内修改逻辑:
class Loop {
  clock = new Clock()
  fpsElement = document.getElementById('fps') as HTMLParagraphElement

  constructor() {
    window.addEventListener('message', (e) => {
      if (e.data === 'tick') {
        this.updateFrame()
      }
    })
  }

  private updateFrame() {
    this.clock.tick()
    this.fpsElement.innerText = this.clock.getTickSmoothFPS().toFixed(2)
  }
}

window.onload = () => {
  const loop = new Loop()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 22:44:59