Предзагрузка изображений средствами JavaScript

275
10 апреля 2017, 06:45

Для игры нужно подгрузить заранее в районе 12 изображений, причем эти изображения могут варьироваться от случая к случаю. То есть сейчас мне нужно подгрузить одни 12 изображений (SVG), а завтра нужно будет другие 12. Поэтому вариант помещения их в <div style="display: none;"><img...></div> не подходит, ибо в этом случае придется постоянно грузить все изображения, а понадобятся мне из них всего 12. Поэтому было решено подгружать их средствами JavaScript перед началом игры. Для этого я реализовал небольшой класс "AssetsPreloader", который, пока что, имеет единственный метод "preload", который принимает массив из объектов вида {src: "ссылка", id: "id"} и подгружает необходимые изображения путем простого создания экземпляра класса Image() (нативный).

class AssetsPreloader {
  static preload(arr) {
    arr.map((a) => {
      let img = new Image(); img.src = a.src; img.id = a.id;
      (this.assets = this.assets || []).push(img);
    });
  }
}

Все подгруженные изображения в итоге попадают в статическое свойство assets этого класса.

Следующим в коде идет отрисовка первого кадра игры, где эти изображения уже используются. И, в общем-то, проблема в том, что отрисовка не происходит. До нее выполнение кода доходит, это я проверял, и после нее код выполняется, но сами изображения не отрисовываются. Насколько я понимаю, происходит это потому, что изображения не успевают загрузиться (даже несмотря на то, что загрузка их происходит с диска). Однако, если запросить отрисовку первого кадра прямо из консоли браузера, то все работает как надо и кадр рисуется нормально.

Весь код в общем и целом выглядит примерно так:

class AssetsPreloader {
  static preload(arr) {
    arr.map((a) => {
      let img = new Image(); img.src = a.src; img.id = a.id;
      (this.assets = this.assets || []).push(img);
      this.done = true;
    });
  }
}
AssetsPreloader.preload([
  {src: "images/image.svg", id: "id_of_image"},
  ...
]);
// ...
GAME.field.draw();

В итоге, вопрос сводится к тому, как максимально правильно подгружать изображения (да и вообще различного рода файлы) так, чтобы, пока они не загрузились, код далше не выполнялся. Причем, возможно, на экране в этот момент будет какой-нибудь прогресс-бар (то есть полностью фризиться скрипт тоже не должен). Первым делом мне в голову пришло добавить весь остальной код в коллбэк к методу preload(), но эта идея успехом не увенчалась.

Прошу помощи. Интересует не только как, но и как наиболее правильно (best-practice, все такое).

P.S. Файл скрипта подключен к странице с атрибутом defer. При использовании, например, async, отрисовка либо происходит, либо нет (отчего зависит - не могу сказать). А вешать в сам код обработчики проверки загрузки страницы, готовности DOM или что-то в этом роде не хочу.

Answer 1

Привожу кусок своего кода предзагрузки изображений. Принимает в качестве аргумента функцию handler которая выполнится когда все изображения загрузятся (или сообщат о сбое при попытки загрузки изображения). Специально привожу в неизменённом состоянии, точно работающем правильно. .preloader меняется с использованием jquery, если он не нужен, можно убрать. Остальное нативный javascript.

Slider3D.prototype.loadAllImages = function( handler ) {
    var self = this;
    var currentImageLoadedIndex = 0;
    for ( var index = 0; index < self.settings.images.length; index++ ) {
        self.data.images[index] = new Image();
        self.data.images[index].onload = self.data.images[index].onerror = function(){
            if ( ++currentImageLoadedIndex >= self.settings.images.length ) {
                self.settings.element.find('.preloading').hide();
                if ( typeof handler === 'function' ) handler();
            } else {
                self.settings.element.find('.preloading').css('width',((currentImageLoadedIndex/self.settings.images.length)*100) + '%');
            }
        };
        self.data.images[index].src = self.settings.images[index];
    }
};
READ ALSO
Нужен скрипт который при нажатии на блок с одним классом закроет другой [требует правки]

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

Ситуация такая: есть слайдер, в котором есть кнопка, после нажатия на нее должен скрываться блок с другим классом, который вне слайдераПомогите...

198
Слайдер Swiper не работает на сенсорных экранах

Слайдер Swiper не работает на сенсорных экранах

На десктопах всё отлично работает, но на сенсорных экранах нетПодключал так:

286
Записать номер массива

Записать номер массива

Каким образом можно записать номер массива из этого участка кода?

202
Не пойму, почему Ajax JQuery работает так

Не пойму, почему Ajax JQuery работает так

Я не могу понять, почему он выполняет сначала 1, потом 3, потом 2?

217