TypeError:无法执行' drawImage' on' CanvasRenderingContext2D':提供的值不是'(CSSImageValue或HTMLImageElement)

时间:2018-05-02 01:31:20

标签: javascript canvas ecmascript-6 drawimage

在turorial之后使用完全相同的代码,但在尝试将图像绘制到画布时将此错误发送到控制台:

  

SpriteSheet.js:30 Uncaught(在promise中)TypeError:无法执行' drawImage' on' CanvasRenderingContext2D':提供的值不是'(CSSImageValue或HTMLImageElement或SVGImageElement或HTMLVideoElement或HTMLCanvasElement或ImageBitmap或OffscreenCanvas)

不知道为什么会这样?第一个图像在画布上绘制得很好但第二个图像没有显示并出现此错误。使用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);
});

2 个答案:

答案 0 :(得分:1)

取代了答案:

错误号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语句永远不会执行并生成控制台消息。

  

return返回语句[Learn More]

后无法访问的代码

因为警告是非致命的,所以函数返回时没有错误。

此错误的第一个修复是删除无法访问的代码。如下一个错误所述,需要另外两个修复程序。

  • 将加载的URL更改为精灵表文件(仍有待准备),
  • loadImage
  • 的承诺处理程序中定义“海洋”和“地面”图块

第2号错误。

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

实际上,在SpriteSheet实例中定义一个图块会将一个图块从精灵图像复制到一个名为Canvas的新对象中,以供绘图函数使用。

根据设计使用精灵表格的建议补救措施是创建一个背景精灵表单图像,其中至少包含两个海洋和地面的16x16图块。它们在精灵图片图像中的位置(计算左侧和上下的图块)决定了如何在loadBackgroundSprites中对图块定义进行编码。

对于此解决方案,请删除对dirtSprite的代码引用。并删除对“SEA01.png”和“ground.png”的图像引用 - loadBackgroundSprites应该加载包含两个图块的组合精灵表的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)
  });

<小时/> 我希望这对教程有帮助。

答案 1 :(得分:0)

我知道这已经有一段时间了。但是,当您在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
}