TypeError: Не удалось выполнить 'drawImage' для 'CanvasRenderingContext2D': предоставленное значение не относится к типу '(CSSImageValue или HTMLImageElement

1

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

SpriteSheet.js: 30 Неподготовлено (в обещании) TypeError: Не удалось выполнить 'drawImage' в 'CanvasRenderingContext2D': предоставленное значение не относится к типу '(CSSImageValue или HTMLImageElement или SVGImageElement или HTMLVideoElement или HTMLCanvasElement или ImageBitmap или OffscreenCanvas)

Не знаю, почему это происходит? Первое изображение хорошо нарисовано на холсте, но второе не появляется и не получает эту ошибку. Используя обещания, чтобы все изображения были загружены перед их использованием, не так ли? Он отлично работает, если я изменяю конец файла 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
    function loadBackgroundSprites имеет два оператора возврата, второй из которых генерирует предупреждение «недоступный код» (код для загрузки и создания «наземного» спрайта не выполняется). Это связано с проблемой? Вы получаете другие ошибки консоли?
  • 0
    Это определенно связано с тем, что упомянул @ traktor53: this.tiles.get('ground') вернет undefined, потому что loadImage('ground.png') никогда не будет вызываться.
Показать ещё 1 комментарий
Теги:
ecmascript-6
canvas
drawimage

2 ответа

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);
  });
}

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

недостижимый код после оператора возврата [Подробнее]

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

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

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

Ошибка №2.

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

Фактически, определение фрагмента в экземпляре SpriteSheet копирует одну плиту из образа листа спрайта в новый объект с именем Canvas для использования с помощью функций рисования.

Предлагаемое средство для работы со списками спрайтов, как было разработано, состоит в том, чтобы создать изображение листа спрайтов с фоном, которое содержит по крайней мере две 16х16 плитки для океана и земли. Их позиции в изображении листа спрайта (подсчет плиток слева и поперек) определяют, как определение фрагмента должно быть закодировано в 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)
  });


Надеюсь, что это поможет в учебнике.
  • 0
    Но как я могу заставить объект спрайтов содержать плитку для ground ? Обе функции loadDirtSprite() определяют основу для объекта спрайтов. Почему же тогда он не работает?
  • 0
    Я снова обновил свой OP с помощью рефакторинга кода. Та же ошибка .. Я чувствую, что сдаюсь.
Показать ещё 5 комментариев
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
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню