Загружайте изображения по одному, используя Promises и async-await в JavaScript - PullRequest
0 голосов
/ 10 ноября 2018

Желаемый результат: Я хочу, чтобы изображения (изображение и изображение2) загружались по одному . Вот мой код:

'use strict';

window.addEventListener('DOMContentLoaded', () => {
  const cvs = document.querySelector('#cvs');
  cvs.width = window.innerWidth;
  cvs.height = window.innerHeight;
  const c = cvs.getContext('2d');

  let image = new Image(); // first image
  image.src = 'https://images5.alphacoders.com/559/559956.jpg';
  let image2 = new Image(); // second image
  image2.src = 'https://www.highreshdwallpapers.com/wp-content/uploads/2013/03/Avengers-A.jpg';
  let y = 0;

  let loader = img => new Promise(resolve => img.onload = resolve(img, 0, y, 100, 60));
  let logger = img => new Promise(resolve => img.onload = resolve(img.src + ' loaded!'));

  async function loadImages() {
    await logger(image).then(console.log); // this works
    await logger(image2).then(console.log); // this works

    await loader(image).then(c.drawImage);
    y += 60;
    await loader(image2).then(c.drawImage);
  };
  
  loadImages();
});
body {
  margin: 0;
}

#cvs {
  position: fixed;
}
<!DOCTYPE html>
<html>

<head>
  <title>Page Title</title>
  <link rel="stylesheet" type="text/css" href="./style.css">
</head>

<body>
  <canvas id='cvs'>Canvas not supported</canvas>
  <script type="text/javascript" src='./script.js'></script>
</body>

</html>
<ч />

Вот моя концепция:

Я создаю функцию, которая возвращает new Promise, который resolves, когда изображение loads, и затем рисует его в контексте холста:
let loader = img => new Promise(resolve => img.onload = resolve(img, 0, height, 100, 60));

Затем я создаю asynchronous функцию, которая вызывает loader для каждого изображения и рисует его на холсте:
async function loadImages() {
    // ...
    await loader(image).then(c.drawImage);
    y += 60;
    await loader(image2).then(c.drawImage);
};
<ч />

Но по некоторым причинам код не работает. И я получаю следующую ошибку: Uncaught (in promise) TypeError: Illegal invocation.

Я пытался заменить c.drawImage на:

c.drawImage.bind(c), как рекомендовано в этом посте и,

c.drawImage.call, в этом случае я изменил свой resolve на resolve(c, img, 0, height, 100, 60)

Но ни один из них не сработал!

Что я делаю не так?

1 Ответ

0 голосов
/ 10 ноября 2018

Ваш loadImages возвращает undefined (поскольку вы ничего не возвращаете явно), поэтому drawImage ничего не сделает.

Даже если не совсем понятно, что вы хотите сделать, у меня естьощущение, что вы хотите нарисовать оба изображения в этом then().

Чтобы сделать как можно меньше изменений в вашем коде, вы можете просто вернуть оба изображения в массиве, а затем выполнить итерациюэтот массив и вызовите drawImage для него.
Вы также должны передать все свои аргументы в виде массива в функции loader.

Также обратите внимание, что в img.onload = resolve(.. была опечатка,это должно быть img.onload = e => resolve(..., в противном случае resolve вызывается напрямую.

И помните, что событие onload сработает только один раз, поэтому, как только вы его дождетесь, оно больше не повторится (если вы не принудительно)это к).

window.addEventListener('DOMContentLoaded', () => {
  const cvs = document.querySelector('#cvs');
  cvs.width = window.innerWidth;
  cvs.height = window.innerHeight;
  const c = cvs.getContext('2d');

  let image = new Image(); // first image
  image.src = 'https://images5.alphacoders.com/559/559956.jpg';
  let image2 = new Image(); // second image
  image2.src = 'https://www.highreshdwallpapers.com/wp-content/uploads/2013/03/Avengers-A.jpg';
  let height = 0;

  let loader = img => new Promise(resolve => {
    // resolve the arguments as an Array
    img.onload = e => resolve([img, 0, height, 100, 60]);
    // force resetting the src, otherwise onload may already have fired
    img.src = img.src;
  });

  async function loadImages() {
    const a = await loader(image);
    height += 60;
    const b = await loader(image2);
    // you must return something if you it to be passed in the then()
    return [a, b];
  };

  loadImages().then(arr => {
    arr.forEach(args => c.drawImage.apply(c, args));
  }).catch(console.error);
});
<canvas id='cvs'>Canvas not supported</canvas>

Но если я могу, ваш код далеко не ясен ... Вы, вероятно, выиграете, если будете более многословным, и разделите свою логику по-другому:
Сначала разберитесь с загрузкой ресурсов, затем разберитесь с вашими объектами рендеринга (здесь аргументы, которые вы передаете drawImage).

Сочетание обоих просто усложнит поддержку вашего кода.

Кроме того, вашу переменную с именем height, вероятно, лучше назвать y.

...