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

如何优化JavaScript Canvas 2D车辆动画 解决卡顿崩溃问题

问题根因

你现在的代码卡顿、崩溃完全是冗余逻辑导致的,核心问题有三个:

  • 无意义的内存占用:你循环生成了187个独立的CanvasPattern对象存在数组里,实际上createPattern生成的平铺图案本身就支持无限重复,根本不需要为每一帧的偏移单独创建pattern对象,上百个冗余的图案对象占了大量内存,是页面崩溃的主要原因。
  • 动画调度方式错误:用setInterval做动画无法和浏览器的渲染刷新率同步,很容易出现帧堆积、掉帧,是卡顿的核心诱因。
  • 重复绘制冗余:你在预加载阶段循环往离屏canvas上反复叠画同一张背景图,这步操作完全没有意义,平白增加了初始化的耗时。
优化后代码

替换你原来的drive_mk2.js内容即可,HTML部分不需要改动,注意不要用CSS强制拉伸canvas尺寸,canvas的渲染分辨率必须通过标签上的width/height属性设置,用CSS改宽高只会拉伸已渲染的内容,既模糊又耗性能。

document.addEventListener("fullscreenchange", function() {
  console.log('Fullscreen Change');
});

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
// 初始设置画布尺寸
resizeCanvas();
// 监听窗口变化自适应尺寸
window.addEventListener('resize', resizeCanvas);

function resizeCanvas() {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
}

// 资源与状态变量
let carImg, bgPattern;
let bgOffset = 0;
// 每帧背景滚动速度,改这个值可以调整车辆行驶的视觉速度
const SCROLL_SPEED = 16;
const BG_WIDTH = 4096;
let animationId = null;

// 加载背景图
const bgImg = new Image();
bgImg.addEventListener('load', function() {
  // 仅需创建1个平铺pattern,不需要预生成上百个冗余对象
  bgPattern = ctx.createPattern(bgImg, 'repeat-x');
  // 背景加载完成后加载车辆图
  carImg = new Image();
  carImg.addEventListener('load', function() {
    // 保留原逻辑:延迟3秒启动动画
    setTimeout(startAnimation, 3000);
  });
  carImg.src = 'img.png';
});
bgImg.src = 'background.jpg';

function startAnimation() {
  // 先停止可能存在的旧动画,避免重复执行
  if (animationId) cancelAnimationFrame(animationId);
  function frame() {
    // 清空上一帧内容
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    // 绘制滚动背景:通过上下文平移实现偏移,不需要更换pattern
    ctx.save();
    ctx.translate(-bgOffset, 0);
    ctx.fillStyle = bgPattern;
    ctx.fillRect(bgOffset, 0, canvas.width + BG_WIDTH, canvas.height);
    ctx.restore();

    // 绘制固定位置的车辆
    ctx.drawImage(carImg, 20, canvas.height - 200);

    // 更新偏移量,超出背景宽度后重置,实现无缝循环
    bgOffset += SCROLL_SPEED;
    if (bgOffset >= BG_WIDTH) bgOffset = 0;

    // 调度下一帧,自动和浏览器屏幕刷新率同步
    animationId = requestAnimationFrame(frame);
  }
  frame();
}

// 保留原逻辑:点击画布停止动画
canvas.addEventListener('click', function(){
  if (animationId) {
    cancelAnimationFrame(animationId);
    animationId = null;
  }
});
实现说明
  • 内存占用直接降到原版本的几十分之一,从根源解决页面崩溃问题。
  • 用requestAnimationFrame替代setInterval做动画调度,自动适配屏幕60Hz/高刷刷新率,不会出现掉帧、帧堆积的问题。
  • 没有改动原有的交互逻辑:3秒延迟启动、点击画布停止、车辆位置、滚动速度都和你原来的效果完全一致。
  • 新增窗口尺寸自适应,浏览器窗口大小变化时自动调整canvas分辨率,不会出现拉伸模糊的问题。
后续可优化方向
  • 把背景图、车辆图压缩到实际显示需要的尺寸,比如页面高度如果只有800px,背景图高度做700-800px就足够,不要用远超显示需求的超大原图,能大幅降低显存占用。
  • 如果后续要加路面障碍物、其他车辆等元素,记得先判断元素位置,只绘制当前在画布可视区域内的内容,不要渲染画布外的元素。
  • 如果需要做加速、减速效果,直接修改SCROLL_SPEED变量的值即可,数值越大背景滚动越快,车辆视觉行驶速度越高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 00:57:19