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
相关产品推荐
相关产品推荐

