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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 00:54:56