CanvasRenderingContext2D.drawImage报错:传入图像损坏问题求助
Canvas绘制图片报错:Passed-in image is "broken" 解决方法
问题描述
跟着YouTube上Chris Courses的教程开发垂直平台游戏时,遇到Canvas绘制图片的报错:
Uncaught DOMException: CanvasRenderingContext2D.drawImage: Passed-in image is "broken"
报错指向以下代码行:
- Sprite类draw方法中的
c.drawImage(this.image, this.position.x, this.position.y) - Sprite类update方法中的
this.draw() - animate函数中的
window.requestAnimationFrame(animate)和background.update()
创建背景Sprite的代码如下:
const background = new Sprite({ position: { x:0, y:0 }, imageSrc: './sprites/bg.png', })
已尝试更换不同图片、检查路径,但问题仍未解决,完整代码如下:
const canvas = document.querySelector('canvas'); const c = canvas.getContext('2d'); canvas.width = 1024; canvas.height = 576; c.fillStyle = 'green'; c.fillRect(0, 0, canvas.width, canvas.height); const gravity = 0.3; class Sprite { constructor({position, imageSrc}){ this.position = position; this.image = new Image(); this.image.src = imageSrc; } draw(){ if (!this.image){ return; } c.drawImage(this.image, this.position.x, this.position.y); } update(){ this.draw(); } } class Player { constructor(position){ this.position = position; this.velocity = { x:0, y:1 }; this.height = 50; } draw(){ c.fillStyle = 'black'; c.fillRect(this.position.x, this.position.y, 50, this.height); } update(){ this.draw(); this.position.y += this.velocity.y; this.position.x += this.velocity.x; if (this.position.y + this.height + this.velocity.y < canvas.height) { this.velocity.y += gravity; } else{ this.velocity.y = 0; } } } const player = new Player({ x:10, y:10, }); let lastKey; const keys = { a: { pressed: false }, d: { pressed: false }, w: { pressed: false } } const background = new Sprite({ position: { x:0, y:0 }, imageSrc: './sprites/bg.png', }) function animate() { window.requestAnimationFrame(animate); c.fillStyle ='green'; c.fillRect(0,0,canvas.width, canvas.height); background.update(); player.update(); player.velocity.x = 0; if (keys.a.pressed && lastKey === 'a') { player.velocity.x = -1; } else if (keys.d.pressed && lastKey === 'd') { player.velocity.x = 1; } } animate() window.addEventListener('keydown',(event) =>{ switch (event.key) { case 'd': keys.d.pressed = true; lastKey = 'd'; break; case 'a': keys.a.pressed = true; lastKey = 'a'; break; case 'w': player.velocity.y = -5; break; } console.log(event.key); }) window.addEventListener('keyup',(event) =>{ switch (event.key) { case 'd': keys.d.pressed = false; break; case 'a': keys.a.pressed = false; break; } console.log(event.key); })
问题根源
浏览器加载图片是异步操作,当animate函数启动时,background.image的src刚被设置,图片还未完成下载,此时调用drawImage就会触发“broken image”错误。原代码中的if (!this.image)判断无效,因为this.image是已创建的Image对象,只是还没加载完成。
修复方案
方案1:给Sprite添加加载状态判断
修改Sprite类,监听图片加载完成事件,只有加载完成后才执行绘制:
class Sprite { constructor({position, imageSrc}){ this.position = position; this.image = new Image(); // 标记图片是否加载完成 this.isLoaded = false; // 图片加载完成后更新状态 this.image.onload = () => { this.isLoaded = true; } this.image.src = imageSrc; } draw(){ // 仅当图片加载完成时才绘制 if (!this.isLoaded) return; c.drawImage(this.image, this.position.x, this.position.y); } update(){ this.draw(); } }
方案2:统一预加载所有资源后再启动动画
如果项目中有多个Sprite资源,可统一管理加载进度,确保所有资源就绪后再运行动画:
// 资源计数:根据实际资源数量修改totalAssets let loadedCount = 0; const totalAssets = 1; // 修改Sprite构造函数,添加加载回调 class Sprite { constructor({position, imageSrc, onLoaded}){ this.position = position; this.image = new Image(); this.image.onload = () => { onLoaded && onLoaded(); } this.image.src = imageSrc; } draw(){ // 也可通过image.complete属性判断加载状态 if (!this.image.complete) return; c.drawImage(this.image, this.position.x, this.position.y); } update(){ this.draw(); } } // 创建背景Sprite,传入加载回调 const background = new Sprite({ position: { x:0, y:0 }, imageSrc: './sprites/bg.png', onLoaded: () => { loadedCount++; // 所有资源加载完成后启动动画 if (loadedCount === totalAssets) { animate(); } } }) // 注释掉原来直接调用animate的代码 // animate()
额外排查项
- 检查图片路径:确认
./sprites/bg.png与项目文件结构匹配,可在浏览器Network标签查看图片请求是否返回404 - 确认图片格式:使用浏览器支持的格式(PNG/JPG/WebP等)
- 本地开发用服务器:避免用
file://协议打开文件,部分浏览器会限制图片加载,推荐用VS Code的Live Server插件启动本地服务器
内容的提问来源于stack exchange,提问作者Sal
相关产品推荐
相关产品推荐

