TypeError: Не удалось выполнить 'drawImage' для 'CanvasRenderingContext2D': предоставленное значение не относится к типу '(CSSImageValue или HTMLImageElement - PullRequest
0 голосов
/ 02 мая 2018

После того, как турориал с тем же точным кодом, но выдает эту ошибку на консоль при попытке нарисовать изображение на холсте:

SpriteSheet.js: 30 Uncaught (в обещании) TypeError: Не удалось выполнить 'drawImage' для 'CanvasRenderingContext2D': предоставленное значение не относится к типу '(CSSImageValue или HTMLImageElement, или SVGImageElement, или HTMLVideoElement, или HTMLCanvasitlement, или Screen HTMLCanvasElement * Screen HTMLCanvasElement) или HTMLCanvasElement или Screen * ImageManCanvasElement * Screen HTMLCanvasElement или 1004 *

Не знаю, почему это происходит? Первое изображение отлично рисуется на холсте, но второе не появляется и выдает эту ошибку. Использование обещаний, поэтому все изображения должны быть загружены перед их использованием, верно? Он отлично работает, если я изменю конец файла 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
                ]
            ]
        }
    ]
}

Заранее спасибо.

EDIT:

Я обновил свой код для загрузки изображений в разные функции, но получил ту же ошибку:

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 голосов
/ 15 января 2019

Я знаю, это было довольно давно. Но когда вы создали новое обещание в модуле 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
}
0 голосов
/ 03 мая 2018

замененный ответ:

Ошибка № 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);
  });
}

Строка в середине, помеченная как «плохой отступ», - это то место, где заканчивается оператор return, начинающийся в начале loadBackgroundSprites, и выполнение возвращается к вызывающей стороне. Оператор return после строки «плохой отступ» никогда не выполняется и генерирует консольное сообщение.

& # x26a0; недоступный код после оператора возврата [Узнать больше]

Поскольку предупреждение не является фатальным, функция возвращается без ошибки.

Первое исправление этой ошибки - удалить недоступный код. Два дополнительных исправления необходимы, как обсуждено в следующей ошибке.

  • изменить URL-адрес, загруженный на URL-адрес файла спрайт-листа (еще не готов),
  • определите плитки "океан" и "земля" в обработчике обещаний для loadImage

Ошибка № 2.

Экземпляры класса SpriteSheet содержат одно изображение, переданное в качестве аргумента конструктору. Это изображение должно содержать массив прямоугольных плиток фиксированной ширины и высоты, соответствующих значениям 2-го и 3-го параметров, используемых в вызове конструктора. Это не то, что вы делаете.

На самом деле, определение плитки в экземпляре 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)
  });


Я надеюсь, что это поможет с учебником.
...