TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement
TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement
使用完全相同的代码跟踪 turorial,但在尝试将图像绘制到 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
]
]
}
]
}
提前致谢。
编辑:
我更新了我的代码以在不同的函数中加载图像,但得到了同样的错误:
script.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);
});
}
中间标记为"bad indent"的行是在loadBackgroundSprites
开始处开始的return语句结束并向调用者执行return的地方。 "bad indent" 行之后的 return
语句从不执行并生成控制台消息。
⚠ unreachable code after return statement [Learn More]
因为警告不是致命的,函数 returns 没有错误。
此错误的第一个修复是删除无法访问的代码。如下一个错误所述,还需要两个额外的修复程序。
- 将加载的 URL 更改为 sprite sheet 文件(尚待准备),
- 在
loadImage
的承诺处理程序中定义 "ocean" 和 "ground" 磁贴
错误编号 2。
SpriteSheet
class 的实例包含单个图像,作为参数传递给构造函数。此图像应包含固定宽度和高度的矩形图块数组,与构造函数调用中使用的第二个和第三个参数值相匹配。这不是你在做什么。
实际上,在 SpriteSheet
实例中定义一个图块会将单个图块从 sprite sheet 图像复制到一个名为 Canvas 的新对象中,供绘图函数使用。
按照设计使用 sprite sheets 的建议补救措施是创建一个背景 sprite sheet 图像,其中至少包含海洋和地面的两个 16x16 图块。它们在 sprite sheet 图像中的位置(计算横向和自上而下的图块)决定了应如何在 loadBackgroundSprites
.
中对图块定义进行编码
对于此解决方案,删除对 dirtSprite
的代码引用。并删除对 "SEA01.png" 和 "ground.png" 的图像引用 - loadBackgroundSprites
应该为包含两个图块的组合精灵 sheet 加载 URL。
在 Promise.all
链中使用 forEach
版本的代码根据 json 文件渲染精灵:
Promise.all([
loadBackgroundSprites(),
loadLevel('1-1')
]).then(([sprites,level]) => {
console.log(level);
level.backgrounds.forEach(background => {
drawBackground(background, context, sprites)
});
希望对本教程有所帮助。
我知道这是很久以前的事了。但是当你在 loader.js 模块中创建一个新的承诺时 - 确保添加一个 .catch((err) => console.log(err))
此外,如果您在更新后仍然遇到问题,请尝试按 Ctrl+Shift+R 刷新静态 http 缓存,它可能仍在缓存您的旧代码。如果这有帮助,请告诉我!
loader.js
export function loadImage(url) {
return new Promise(resolve => {
const image = new Image();
image.addEventListener('load', () => {
resolve(image);
});
image.src = url;
}).catch((err) => console.log(err)); <--------ADD THIS
}
使用完全相同的代码跟踪 turorial,但在尝试将图像绘制到 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
]
]
}
]
}
提前致谢。
编辑:
我更新了我的代码以在不同的函数中加载图像,但得到了同样的错误:
script.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);
});
}
中间标记为"bad indent"的行是在loadBackgroundSprites
开始处开始的return语句结束并向调用者执行return的地方。 "bad indent" 行之后的 return
语句从不执行并生成控制台消息。
⚠ unreachable code after return statement [Learn More]
因为警告不是致命的,函数 returns 没有错误。
此错误的第一个修复是删除无法访问的代码。如下一个错误所述,还需要两个额外的修复程序。
- 将加载的 URL 更改为 sprite sheet 文件(尚待准备),
- 在
loadImage
的承诺处理程序中定义 "ocean" 和 "ground" 磁贴
错误编号 2。
SpriteSheet
class 的实例包含单个图像,作为参数传递给构造函数。此图像应包含固定宽度和高度的矩形图块数组,与构造函数调用中使用的第二个和第三个参数值相匹配。这不是你在做什么。
实际上,在 SpriteSheet
实例中定义一个图块会将单个图块从 sprite sheet 图像复制到一个名为 Canvas 的新对象中,供绘图函数使用。
按照设计使用 sprite sheets 的建议补救措施是创建一个背景 sprite sheet 图像,其中至少包含海洋和地面的两个 16x16 图块。它们在 sprite sheet 图像中的位置(计算横向和自上而下的图块)决定了应如何在 loadBackgroundSprites
.
对于此解决方案,删除对 dirtSprite
的代码引用。并删除对 "SEA01.png" 和 "ground.png" 的图像引用 - loadBackgroundSprites
应该为包含两个图块的组合精灵 sheet 加载 URL。
在 Promise.all
链中使用 forEach
版本的代码根据 json 文件渲染精灵:
Promise.all([
loadBackgroundSprites(),
loadLevel('1-1')
]).then(([sprites,level]) => {
console.log(level);
level.backgrounds.forEach(background => {
drawBackground(background, context, sprites)
});
希望对本教程有所帮助。
我知道这是很久以前的事了。但是当你在 loader.js 模块中创建一个新的承诺时 - 确保添加一个 .catch((err) => console.log(err))
此外,如果您在更新后仍然遇到问题,请尝试按 Ctrl+Shift+R 刷新静态 http 缓存,它可能仍在缓存您的旧代码。如果这有帮助,请告诉我!
loader.js
export function loadImage(url) {
return new Promise(resolve => {
const image = new Image();
image.addEventListener('load', () => {
resolve(image);
});
image.src = url;
}).catch((err) => console.log(err)); <--------ADD THIS
}