使用完全相同的代码执行教程,但在尝试将图像绘制到 Canvas 时将此错误发送到控制台:

SpriteSheet.js:30 Uncaught (in promise) TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)

不知道为什么会这样?第一张图片在 Canvas 上绘制得很好,但第二张图片没有显示并出现此错误。使用 promises 以便在使用之前加载所有图像,对吗?如果我将 script.js 文件的末尾更改为 drawBackground(level.backgrounds[1], context, sprites);

,它会正常工作

script.js

import SpriteSheet from './SpriteSheet.js'; 
import {loadImage, loadLevel} from './loaders.js'; 
 
function drawBackground(background, context, sprites) { 
    background.ranges.forEach(([x1, x2, y1, y2]) => { 
        for (let x = x1; x < x2; ++x) { 
            for (let y = y1; y < y2; ++y) { 
                sprites.drawTile(background.tile, context, x, y); 
            } 
        } 
    }); 
} 
 
function loadBackgroundSprites() { 
  return loadImage('SEA01.png').then(image => { 
    const sprites = new SpriteSheet(image, 16, 16); 
    sprites.define('ocean', 0, 0); 
    return sprites; 
}); 
  return loadImage('ground.png').then(image=> { 
    const sprites = new SpriteSheet(image, 16, 16); 
    sprites.define('ground', 12, 0); 
  }); 
} 
 
const canvas = document.getElementById('gamearea'); 
const context = canvas.getContext('2d'); 
 
Promise.all([ 
  loadBackgroundSprites(), 
  loadLevel('1-1') 
]).then(([sprites,level]) => { 
  console.log(level); 
  drawBackground(level.backgrounds[0], context, sprites); 
}); 

loader.js

export function loadImage(url) { 
    return new Promise(resolve => { 
        const image = new Image(); 
        image.addEventListener('load', () => { 
            resolve(image); 
        }); 
        image.src = url; 
    }); 
} 
 
export function loadLevel(name) { 
    return fetch(`/levels/${name}.json`) 
    .then(r => r.json()); 
} 

spritesheet.js

export default class SpriteSheet { 
    constructor(image, w = 16, h = 16) { 
        this.image = image; 
        this.width = w; 
        this.height = h; 
        this.tiles = new Map(); 
    } 
 
    define(name, x, y) { 
        const buffer = document.createElement('canvas'); 
        buffer.height = this.height; 
        buffer.width = this.width; 
        buffer 
            .getContext('2d') 
            .drawImage( 
                this.image, 
                this.width * x, 
                this.height * y, 
                this.width, 
                this.height, 
                0, 
                0, 
                this.width, 
                this.height); 
        this.tiles.set(name, buffer); 
    } 
 
    draw(name, context, x, y) { 
        const buffer = this.tiles.get(name); 
        context.drawImage(buffer, x, y); 
    } 
 
    drawTile(name, context, x, y) { 
        this.draw(name, context, x * this.width, y * this.height); 
    } 
} 

1-1.json

{ 
    "backgrounds": [ 
        { 
            "tile": "ocean", 
            "ranges": [ 
                [ 
                    0, 50, 
                    0, 25 
                ] 
            ] 
        }, 
        { 
            "tile": "ground", 
            "ranges": [ 
                [ 
                    18, 25, 
                    10, 15 
                ] 
            ] 
        } 
    ] 
} 

提前致谢。

编辑:

我更新了我的代码以在不同的函数中加载图像,但得到了同样的错误:

脚本.js

import SpriteSheet from './SpriteSheet.js'; 
import {loadImage, loadLevel} from './loaders.js'; 
 
const canvas = document.getElementById('gamearea'); 
const context = canvas.getContext('2d'); 
 
function drawBackground(background, context, sprites) { 
    background.ranges.forEach(([x1, x2, y1, y2]) => { 
        for (let x = x1; x < x2; ++x) { 
            for (let y = y1; y < y2; ++y) { 
                sprites.drawTile(background.tile, context, x, y); 
            } 
        } 
    }); 
} 
 
function drawMario(background, context, mario) { 
    background.ranges.forEach(([x1, x2, y1, y2]) => { 
        for (let x = x1; x < x2; ++x) { 
            for (let y = y1; y < y2; ++y) { 
                mario.drawTile(background.tile, context, x, y); 
            } 
        } 
    }); 
} 
 
function loadBackgroundSprites() { 
  return loadImage('SEA01.png').then(image => { 
    const sprites = new SpriteSheet(image, 16, 16); 
    sprites.define('ocean', 0, 0); 
    return sprites; 
  }); 
} 
 
function loadDirtSprite() { 
  return loadImage('ground.png').then(image => { 
    const mario = new SpriteSheet(image, 16, 16); 
    mario.define('ground', 12, 0); 
    return mario; 
  }); 
} 
 
 
 
Promise.all([ 
  loadBackgroundSprites(), 
  loadLevel('1-1'), 
  loadDirtSprite() 
]).then(([sprites, level, mario]) => { 
  level.backgrounds.forEach(background => { 
    drawBackground(background, context, sprites, mario); 
  }); 
  mario.draw('ground', context, 1, 16); 
}); 

请您参考如下方法:

替换答案:

错误编号 1。

问题评论中提到,原代码在loadBackgroundSprites中有错误:

function loadBackgroundSprites() { 
  return loadImage('SEA01.png').then(image => { 
    const sprites = new SpriteSheet(image, 16, 16); 
    sprites.define('ocean', 0, 0); 
    return sprites; 
});                                  // <-- bad indent 
  return loadImage('ground.png').then(image=> { 
    const sprites = new SpriteSheet(image, 16, 16); 
    sprites.define('ground', 12, 0); 
  }); 
} 

中间标记为“错误缩进”的行是在 loadBackgroundSprites 开头开始的 return 语句结束并且执行返回给调用者的地方。 “错误缩进”行之后的 return 语句永远不会执行并生成一条控制台消息。

⚠ unreachable code after return statement [Learn More]

因为警告不是致命的,函数返回没有错误。

此错误的第一个修复是删除无法访问的代码。正如下一个错误中所讨论的那样,还需要进行两个额外的修复。

  • 将加载的 URL 更改为 sprite 表文件的 URL(尚待准备),
  • loadImage 的 promise 处理程序中定义“海洋”和“地面”图 block

错误编号 2。

SpriteSheet 类的实例包含一个图像,作为参数传递给构造函数。此图像应包含一组固定宽度和高度的矩形图 block ,这些矩形图 block 与构造函数调用中使用的第二个和第三个参数值相匹配。这不是你在做什么。

实际上,在 SpriteSheet 实例中定义一个图 block 会将单个图 block 从 sprite 表图像复制到一个新的命名 Canvas 对象中,以供绘图函数使用。

按照设计使用 sprite 表的建议补救措施是创建一个背景 sprite 表图像,其中至少包含海洋和地面的两个 16x16 block 。它们在 sprite 表图像中的位置(计算从左到右和自上而下的图 block )决定了在 loadBackgroundSprites 中应如何对图 block 定义进行编码。

对于此解决方案,删除对 dirtSprite 的代码引用。并删除对“SEA01.png”和“ground.png”的图像引用 - loadBackgroundSprites 应该加载包含两个图 block 的组合 Sprite 表的 URL。

Promise.all 链中使用 forEach 版本的代码根据 json 文件渲染 Sprite :

Promise.all([ 
  loadBackgroundSprites(), 
  loadLevel('1-1') 
]).then(([sprites,level]) => { 
  console.log(level); 
  level.backgrounds.forEach(background => { 
    drawBackground(background, context, sprites) 
  }); 


我希望对本教程有所帮助。


评论关闭
IT干货网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!