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

JavaScript Canvas多次绘制同一图像仅显示一个如何解决

问题根源

遍历道具实例绘制却只显示1个道具,核心是两个逻辑错误:

  • 所有道具实例共用同一个itemsImage图片对象,而你在Sprite构造函数中直接给image.onload赋值回调。onload是元素的属性而非事件栈,重复赋值会覆盖之前绑定的所有回调,只有最后一个创建的道具实例能触发尺寸计算逻辑,拿到正确的width/height属性,其余实例的这两个值始终为undefined,导致drawImage接收到非法参数后静默失败,无法渲染。
  • 动画循环启动时机早于图片资源加载完成,首帧渲染时部分Sprite实例的尺寸属性还未初始化,直接调用绘制方法会执行失败。
  • 额外注意:当前itemsData中3个值为486的道具都在地图第一行,x坐标间隔仅12px,若道具单帧宽度为12px,三个道具会紧密相邻,视觉上也容易被误认为是单个道具。
修复步骤
  1. 重构Sprite类的尺寸计算逻辑,抽离独立的尺寸计算方法,不要在构造函数中覆写共享图片对象的onload属性,绘制前增加尺寸校验兜底。
  2. 增加全局资源加载等待逻辑,所有图片资源加载完成后,统一初始化所有Sprite实例的尺寸,再启动动画循环。
修正后的核心代码
//class Sprite
class Sprite {
  constructor({
    position,
    image,
    frames = {
      currentH: 0,
      currentV: 0,
      maxH: 1,
      maxV: 1
    }
  }) {
    this.position = position;
    this.image = image;
    this.frames = { ...frames, elapsed: 0 };
    this.isMoving = false;
    // 初始化时尝试计算尺寸
    this.calcSize();
  }

  // 抽离尺寸计算逻辑
  calcSize() {
    if (this.image.complete) {
      this.width = this.image.width / this.frames.maxH;
      this.height = this.image.height / this.frames.maxV;
    }
  }

  draw() {
    // 绘制前兜底校验尺寸
    if (!this.width || !this.height) this.calcSize();
    ctx.drawImage(
      this.image,
      this.frames.currentH * this.width,
      this.frames.currentV * this.height,
      this.width,
      this.height,
      this.position.x,
      this.position.y,
      this.width,
      this.height
    )
  }
}

// 其余常量定义、Sprite实例化、地图解析逻辑保持原有代码不变

// 等待所有图片资源加载完成
const gameImages = [bgImage, playerImage, itemsImage];
function loadAllImages() {
  return Promise.all(gameImages.map(img => {
    return new Promise(resolve => {
      if (img.complete) return resolve();
      img.onload = resolve;
      img.onerror = resolve;
    })
  }))
}

// 画布逐帧渲染逻辑
async function animate() {
  // 绘制背景
  background.draw();

  items.forEach(item => {
    item.isMoving = true;
    item.draw();
  });

  // 绘制玩家
  player.draw();

  window.requestAnimationFrame(animate);
}

// 资源加载完成后再启动动画
loadAllImages().then(() => {
  // 统一初始化所有精灵的尺寸
  background.calcSize();
  player.calcSize();
  items.forEach(item => item.calcSize());
  animate();
});
验证说明

修改后可以先打印items数组,确认每个实例的position坐标、width/height属性都有正确值,即可看到多个道具正常渲染。如果觉得道具挨得太近,调整itemsData中486值的分布位置,或修改TILE_WIDTH/TILE_HEIGHT参数即可调整道具间距。

内容的提问来源于stack exchange,提问作者J.Doe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 21:54:28