JavaScript Canvas多次绘制同一图像仅显示一个如何解决
问题根源
遍历道具实例绘制却只显示1个道具,核心是两个逻辑错误:
- 所有道具实例共用同一个
itemsImage图片对象,而你在Sprite构造函数中直接给image.onload赋值回调。onload是元素的属性而非事件栈,重复赋值会覆盖之前绑定的所有回调,只有最后一个创建的道具实例能触发尺寸计算逻辑,拿到正确的width/height属性,其余实例的这两个值始终为undefined,导致drawImage接收到非法参数后静默失败,无法渲染。 - 动画循环启动时机早于图片资源加载完成,首帧渲染时部分Sprite实例的尺寸属性还未初始化,直接调用绘制方法会执行失败。
- 额外注意:当前
itemsData中3个值为486的道具都在地图第一行,x坐标间隔仅12px,若道具单帧宽度为12px,三个道具会紧密相邻,视觉上也容易被误认为是单个道具。
修复步骤
- 重构
Sprite类的尺寸计算逻辑,抽离独立的尺寸计算方法,不要在构造函数中覆写共享图片对象的onload属性,绘制前增加尺寸校验兜底。 - 增加全局资源加载等待逻辑,所有图片资源加载完成后,统一初始化所有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
相关产品推荐
相关产品推荐

