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

Canvas中For循环无法绘制多张图片问题求助

问题排查与修复方案

我帮你梳理了代码里的几个关键问题,这就是你只能看到一张图片的核心原因:

1. 原型方法定义位置错误

你把Usership2.prototype.update和render方法写在了构造函数内部,这会导致每次创建Usership2实例时,都会重新覆盖原型上的方法,不仅逻辑混乱,还会让后续实例的渲染行为异常。必须把原型方法移到构造函数外面。

2. 绘图坐标错误

render方法里调用drawImage时用了未定义的x,y参数,所有图片都会被绘制在同一个位置(默认0,0),看起来就像只显示了一张。应该使用实例自身的this.xtype.x和this.xtype.y作为坐标。

3. 图片未完成预加载就渲染

直接设置图片src后就启动渲染循环,可能图片还没加载完成,此时调用drawImage会失败。需要等待所有图片加载完成后再开始渲染。

4. 关键字误用

按钮的onClick事件里用了delete(),但delete是JavaScript的关键字,不能用作函数名,得改成合法的函数名比如deleteObject()。

修复后的完整代码

<canvas style="z-index:1" width="1024" height="768" id="canvas"></canvas>
<input type="button" style="z-index:2; position:absolute; top:50px; left:10px" value="Create" onClick="createNew()">
<input type="button" style="z-index:2; position:absolute; top:100px; left:10px" value="down" onClick="deleteObject()">

<script>
function createNew() { 
  new Usership2(300,300,'green',0); 
}

// 兼容requestAnimationFrame
window.requestAnimationFrame = window.requestAnimationFrame || 
                               window.webkitRequestAnimationFrame || 
                               window.mozRequestAnimationFrame || 
                               window.msRequestAnimationFrame || 
                               window.oRequestAnimationFrame || 
                               function(f) { window.setTimeout(f, 1000/60); };

var Obj = function (x,y,sp,size){ 
  this.size = size; 
  this.x = x; 
  this.y = y; 
  this.color = sp; 
}

var Usership = function(x,y,sp,imagenumber){ 
  this.depot = 10; 
  this.type = 'Usership'; 
  this.xtype = new Obj(x,y,sp,10); 
  w.objects.push(this);
}
// 把原型方法移到构造函数外
Usership.prototype.update = function () { }
Usership.prototype.render = function () { 
  ctx.save(); 
  ctx.fillStyle = this.xtype.color; 
  ctx.fillRect(this.xtype.x, this.xtype.y, this.xtype.size, this.xtype.size); 
  // 使用实例自身的坐标
  ctx.drawImage(imgObjs[imagenumber], this.xtype.x, this.xtype.y); 
  ctx.restore(); 
}

var Usership2 = function(x,y,sp,imagenumber){ 
  this.depot = 10; 
  this.type = 'Usership'; 
  this.xtype = new Obj(x,y,sp,10); 
  w.objects.push(this);
}
// 把原型方法移到构造函数外
Usership2.prototype.update = function () { }
Usership2.prototype.render = function () { 
  ctx.save(); 
  ctx.fillStyle = this.xtype.color; 
  ctx.fillRect(this.xtype.x, this.xtype.y, this.xtype.size, this.xtype.size); 
  // 使用实例自身的坐标
  ctx.drawImage(imgObjs2[0], this.xtype.x, this.xtype.y); 
  ctx.restore(); 
}

var World = function(){ 
  this.objects = []; 
  this.count = function(type,sp){ 
    var cnt = 0; 
    for(var k = 0; k < this.objects.length; k++) cnt++; 
    return cnt; 
  } 
}

function renderWorld(){ 
  var spliceArray = []; 
  ctx.clearRect(0, 0, canvas.width, canvas.height); 
  ctx.fillStyle = "white"; 
  ctx.fillRect(0, 0, canvas.width, canvas.height); // 正确填充整个画布
  for(let i = 0; i < w.objects.length; i++){ // 使用局部变量避免全局污染
    w.objects[i].update(); 
    w.objects[i].render(); 
    if(w.objects[i].depot < 1) spliceArray.push(i); 
  } 
  // 删除元素时注意索引偏移
  for(var k = spliceArray.length - 1; k >= 0; k--) { 
    w.objects.splice(spliceArray[k],1); 
  } 
  requestAnimationFrame(renderWorld); 
}

const canvas = document.getElementById("canvas"); 
const ctx = canvas.getContext("2d"); 
const width = 1024, height = 768;

// 图片预加载函数
function loadImages(imageUrls) {
  return new Promise((resolve) => {
    const imgObjs = [];
    let loadedCount = 0;
    imageUrls.forEach((url, index) => {
      const img = new Image();
      img.onload = () => {
        loadedCount++;
        if(loadedCount === imageUrls.length) {
          resolve(imgObjs);
        }
      };
      img.src = url;
      imgObjs[index] = img;
    });
  });
}

let imgObjs, imgObjs2;
const w = new World();

// 等待所有图片加载完成后再初始化
Promise.all([
  loadImages(['https://i.sstatic.net/rsH6n.png']),
  loadImages(['https://i.sstatic.net/rsH6n.png'])
]).then(([imgs1, imgs2]) => {
  imgObjs = imgs1;
  imgObjs2 = imgs2;
  // 创建3个实例,调整间距避免重叠
  for (let i=0; i<3; i++) { 
    new Usership2(i*50, i*50, 'green', 0); 
  }
  requestAnimationFrame(renderWorld);
});

// 修复delete关键字问题
function deleteObject() {
  if(w.objects.length > 0) {
    w.objects.pop(); // 示例:删除最后一个对象
  }
}
</script>

额外说明

  • 我调整了实例的间距(从i*10改成i*50),避免矩形和图片重叠太厉害,方便你查看效果;
  • 修复了fillRect填充画布的错误,原来的ctx.fill()没有配合路径使用,改成直接用fillRect填充整个画布;
  • 删除对象时从后往前遍历spliceArray,避免索引偏移导致的删除错误;
  • 使用Promise处理图片预加载,确保所有图片加载完成后再开始渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:45:27