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

Chrome中requestAnimationFrame驱动的Canvas滚动卡顿问题求助

Chrome下requestAnimationFrame滚动时卡顿的解决办法

问题描述

我通过代码显示Canvas的帧间隔dt,当dt>20ms时内容填充红色。正常情况下我的电脑能支持requestAnimationFrame以60fps(约16ms)运行,但在Chrome浏览器中滚动时,画面频繁闪红、卡顿,即使轻微滚动也会触发该问题。绘制性能并非瓶颈,即使绘制大量元素也能流畅运行,仅滚动时会出现几帧卡顿。改用setInterval则无此问题,且Firefox浏览器中不存在该现象,求Chrome下的解决办法。

代码示例

<!DOCTYPE html>
<html>
<style>
  body, html {
  overflow: hidden;
  overscroll-behavior: none;
}
</style>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, maximum-scale=1, minimum-scale=1, viewport-fit=cover">
</head>
<body style="padding:0;margin:0;background-color: #151515;pointer-events: none">
<script>
  
class LCanvas{ 
  constructor(){
    this.canvas = document.createElement("canvas")
    this.ctx = this.canvas.getContext("2d")
    this.canvas.style.position = "fixed"
    this.canvas.style.top = "0px"
    this.canvas.style.left = "0px"
    this.canvas.style.pointerEvents = "none"
    document.body.appendChild(this.canvas)
    return(this)
  }

  fitScreenSize(){
    this.canvas.width = window.innerWidth
    this.canvas.height = window.innerHeight
  }

  clear(){
    this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)
  }
}

var can = new LCanvas()
can.clear()
can.fitScreenSize()
var lastTime = 0

function mainLoop(){
  requestAnimationFrame(mainLoop)
  let now = performance.now()
  let dt = now-lastTime
  lastTime = now
  can.clear()
  can.ctx.font = "40px arial"
  can.ctx.fillStyle = dt>20?"red":"pink"
  can.ctx.fillText(dt,100,100)
  can.ctx.fillRect(400,100,dt*50,100)

}
requestAnimationFrame(mainLoop,true)


</script>

</body>
</html>

解决办法

1. 给Canvas添加合成层优化样式

给Canvas元素添加will-change: transform样式,提示浏览器提前为该元素创建独立的合成层,避免滚动操作触发的重排重绘阻塞动画帧:

canvas {
  will-change: transform;
}

2. 调整requestAnimationFrame的调用位置

将requestAnimationFrame的调用放在帧绘制逻辑的末尾,确保当前帧的所有绘制操作完成后再调度下一帧,减少帧调度冲突:

function mainLoop(){
  let now = performance.now()
  let dt = now-lastTime
  lastTime = now
  can.clear()
  can.ctx.font = "40px arial"
  can.ctx.fillStyle = dt>20?"red":"pink"
  can.ctx.fillText(dt,100,100)
  can.ctx.fillRect(400,100,dt*50,100)
  // 将调度移到绘制逻辑之后
  requestAnimationFrame(mainLoop)
}
requestAnimationFrame(mainLoop)

3. 开启Chrome线程化滚动优化

在Chrome地址栏输入chrome://flags/#enable-threaded-scrolling,开启Threaded Scrolling选项,重启浏览器后,滚动操作会由单独线程处理,降低对主线程动画的阻塞影响。

4. 拆分非关键绘制到空闲回调

如果存在非核心的绘制逻辑,可以将其放到requestIdleCallback中执行,避免占用动画帧的主线程时间:

function mainLoop(){
  requestAnimationFrame(mainLoop)
  let now = performance.now()
  let dt = now-lastTime
  lastTime = now
  can.clear()
  // 优先执行关键绘制逻辑
  can.ctx.font = "40px arial"
  can.ctx.fillStyle = dt>20?"red":"pink"
  can.ctx.fillText(dt,100,100)
  // 非关键逻辑放到浏览器空闲时执行
  requestIdleCallback(() => {
    can.ctx.fillRect(400,100,dt*50,100)
  })
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.03 00:13:10