Асинхронная загрузка изображений

Идея в том, чтобы загружать изображения вне DOM. По-умолчанию показывается прелоадер, а по окончанию загрузки подменяется ссылка на изображение. Так как изображение попадает в кэш браузера, то оно мгновенно появиться у пользователя.

  /**
   * Загрузить изображение асинхронно
   * @param url - image URL
   */
  private _asyncImage(url: string): Promise<HTMLImageElement> {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => resolve(img);
      img.onerror = () => reject(new Error(`Could not load image: ${url}`));
      img.src = url;
    });
  }

  /**
   * Загрузить изображение
   * @param url - image URL
   */
  private loadImageAsync(url: string): Observable<HTMLImageElement> {
    return new Observable(observer => {
      const img = new Image();
      img.onload = () => {
        observer.next(img);
        observer.complete();
      };
      img.onerror = () => observer.error(new Error(`Could not load image: ${url}`));
      img.src = url;
    });
  }

  /**
   * Проверить успешность загрузки изображения.
   * @param img - загружаемое изображения
   */
  private _isImageLoaded(img: HTMLImageElement): boolean {
    if (!img.complete) {
      return false;
    }
    if (img.naturalWidth + img.naturalHeight === 0) {
      return false;
    }
    return true;
  }  

Использование

this._asyncImage(value)
  .then(img => {
    this._isImageLoaded(img) ? this._setBackgroundImage(value) : this._setDefaultImage();
  })
  .catch(() => this._setDefaultImage());

В методах _setDefaultImage и _setBackgroundImage в зависимости от ситуации происходит подмена:

  • src для <img>
  • url() для background-image

Похожие записи

RxJS. Delay from array

import { of, from } from 'rxjs'; 
import { map, concatMap, delay } from 'rxjs/operators';

from([2,4,6,8]).pipe(
  concatMap(item => of(item).pipe(delay(1000)))
).subscribe(console.log);