网络宝典
第二套高阶模板 · 更大气的阅读体验

Canvas绘制卡顿?这5个实操技巧让动画丝滑起来

发布时间:2026-01-25 11:11:26 阅读:21 次

做网页小游戏、数据可视化图表,或者搞个粒子特效,Canvas 是绕不开的工具。但画着画着就发现:帧率掉到 20fps,拖动鼠标像在泥里走——不是电脑太老,大概率是 Canvas 用法出了问题。

别急着换框架,先看看这几处“性能黑洞”

Canvas 本身不慢,慢的是我们反复调用没必要的 API、频繁读写像素、或让浏览器不停重绘整块画布。

1. 避免每帧都清空整个画布

很多新手习惯在动画循环开头写 ctx.clearRect(0, 0, canvas.width, canvas.height)。如果只改了画面右下角一个小圆,却把整个 1920×1080 的区域都擦一遍,纯属浪费。

更省的办法:只清除变动区域,或者用背景图覆盖旧内容;动画元素少时,甚至可以跳过清空,直接重绘新位置(比如小球移动,只擦掉上一帧的小球,再画新位置)。

2. 合理使用离屏 Canvas

复杂图形(比如带阴影、渐变、多层叠加的文字)反复绘制很耗时。把它画到一个隐藏的 <canvas> 上,之后只需用 drawImage() 复制过去,效率翻倍。

const offscreen = document.createElement('canvas');
offscreen.width = 200;
offscreen.height = 100;
const offCtx = offscreen.getContext('2d');
// 一次性绘制复杂内容
offCtx.shadowBlur = 10;
offCtx.shadowColor = '#000';
offCtx.font = 'bold 24px Arial';
offCtx.fillText('高性能', 20, 60);
// 动画中只需:
ctx.drawImage(offscreen, x, y);

3. 少用 getImageData()putImageData()

这两个方法会把像素数据搬进搬出内存,一帧调一次,1080p 下就是 200 万次操作。真要处理像素,尽量批量做,或改用 WebGL;日常动画完全没必要碰它。

4. 控制绘制频率,别盲目 requestAnimationFrame

不是所有场景都要 60fps。比如仪表盘指针转动、温度曲线缓慢更新,30fps 足够,还省电。可以用时间戳控制实际绘制间隔:

let lastDraw = 0;
function animate(timestamp) {
if (timestamp - lastDraw > 33) { // 约 30fps
draw();
lastDraw = timestamp;
}
requestAnimationFrame(animate);
}

5. 图片加载完成再画,别留空白帧

new Image() 加载贴图时,如果还没 onload 就执行 drawImage(),Canvas 会静默失败,还可能触发重排。务必等图片加载完毕:

const img = new Image();
img.src = 'icon.png';
img.onload = () => {
// 这里才开始动画循环
requestAnimationFrame(drawLoop);
};

另外,大图记得用 image.decode() 提前解码(支持较新浏览器),避免首次绘制时卡顿。

Canvas 性能优化没有银弹,但多数卡顿,往往就藏在这几个“顺手就写”的细节里。改完试试看,FPS 计数器跳上去那一刻,比喝冰可乐还爽。