Angular 2 - Предварительная загрузка фонового изображения?

У меня есть этот проект angular, где у меня есть большое фоновое изображение, которое заполняет страницу и простую боковую панель ссылками, которые при щелчке меняют URL-адрес фона с другим изображением (из cdn). Поскольку эти изображения довольно большие, они занимают секунду или два, чтобы загрузить, и это заметно, я хочу добавить прелоадер, но я не уверен, как это будет сделано в angular 2.

В моем html у меня есть это:

<section class="fullsizebg image-bg" [ngStyle]="{'background-image': 'url(' + urlImage + ')'}"></section>

Переменная urlImage заполняется в конструкторе компонента, а ссылки боковой панели меняют значение его на клик с помощью простой функции, например:

generateImage(data: any){
    this.urlImage = 'http://example.com/mycdn/'+this.data.url+'.jpg';
}

Таким образом, URL-адрес фактически изменяется мгновенно, но изображение немного загружается. Я бы хотел добавить gip для загрузки или что-то в этом роде, чтобы изображение не изменилось для пользователя и не было так нервным, как сейчас.

Ответ 1

Один из способов сделать это - использовать Blob, чтобы получить изображение и сохранить его в компоненте img, таким образом, у вас есть руки в процессе загрузки, и вы можете добавить свой загрузочный файл gif:

@Component({
   selector:'loading-image',
   template:'<img alt="foo" [src]="src"/>'
})
export class ExampleLoadingImage{

   public src:string = "http://example.com/yourInitialImage.png";

   constructor(private http:Http){}

   generateImage(data: any): void {
      this.src = 'http://www.downgraf.com/wp-content/uploads/2014/09/01-progress.gif'; //Just a random loading gif found on google.
      this.http.get('http://example.com/mycdn/'+this.data.url+'.jpg')
         .subscribe(response => {
            let urlCreator = window.URL;
            this.src = urlCreator.createObjectURL(response.blob());
         });
    }
}

N.B: вы должны ввести свой параметр данных, типизация - это хороший способ обеспечить согласованность над вашим кодом, any следует использовать только как джокер, например Object в Java.

Ответ 2

Это решение использует то, что angular и браузер уже предоставляют. Загрузка изображения выполняется браузером, и вам не нужно возиться с любыми данными или DOM самостоятельно.

Я тестировал это на Chrome 53 и работал безупречно.

Это ваш элемент, который изменил его фон:

<div class="yourBackgroundClass" [style.background-image]="'url(' + imgUrl + ')'"></div>

Для предварительной выборки изображения используется тег изображения, который не отображается. Было бы неплохо дополнительно сделать его position: absolute и вывести его из представления или сделать его действительно крошечным, чтобы он не мог помешать вашему фактическому контенту.

<img [src]="imgPreloadUrl" (load)="imgUrl = imgPreloadUrl" hidden>

С помощью параметра imgPreloadUrl img src обновляется angular, и браузер загружает изображение в невидимый тег img. После этого onload срабатывает, и мы устанавливаем imgUrl = imgPreloadUrl. angular теперь обновляет style.background-image фактического фона, а фоновые изображения переключаются немедленно, потому что он уже загружен в скрытое изображение.

Пока imgUrl !== imgPreloadUrl мы можем показать счетчик, чтобы указать загрузку:

<div class="spinner" *ngIf="imgUrl !== imgPreloadUrl"></div>

проверить:

<button (click)="imgPreloadUrl = 'https://upload.wikimedia.org/wikipedia/commons/2/24/Willaerts_Adam_The_Embarkation_of_the_Elector_Palantine_Oil_Canvas-huge.jpg'">test</button>

Ответ 3

Использование объекта изображения ( Демо-версия плунжера и neArr;)

tmpImg: HTMLImageElement; // will be used to load the actual image before showing it

generateImage(data: any){
 this.urlImage = 'http://example.com/mycdn/'+ 'loading_GIF_url';  // show loading gif

 let loaded = () => { // wait for image to load then replace it with loadingGIF
   this.urlImage = 'http://example.com/mycdn/' + this.data.url+'.jpg';
 }

 // background loading logic
 if(this.tmpImg){
   this.tmpImg.onload = null; // remove the previous onload event, if registered
 }
 this.tmpImg = new Image();
 this.tmpImg.onload = loaded;  // register the onload event
 this.tmpImg.src = 'http://example.com/mycdn/'+this.data.url+'.jpg';
}

Ответ 4

Есть несколько вещей, которые вам нужны, такие как http, resolver и sanitizer. здесь ссылка на это объясняет, как реализовать это с нуля.

Например, у вас есть запрос на преобразование возвращенного большого двоичного объекта в безопасный стиль, чтобы мы могли использовать его в нашей директиве стиля

this.http.get('assets/img/bg.jpg', { responseType: 'blob' }).pipe(
  map( image => {
    const blob: Blob = new Blob([image], { type: 'image/jpeg' });
    const imageStyle = 'url(${window.URL.createObjectURL(blob)})';
    return this.sanitizer.bypassSecurityTrustStyle(imageStyle);
  })
)

Ответ 5

Недавно я реализовал это как структурную директиву:

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({ selector: '[appPreloadImage]'})
export class PreloadImageDirective {

  @Input("appPreloadImage") imageUrl : string;

  constructor( private templateRef : TemplateRef<any>,
               private viewContainer : ViewContainerRef) {
  }

  private showView() {
    this.viewContainer.createEmbeddedView(this.templateRef);
  }

  ngOnInit() {
    var self = this;
    self.viewContainer.clear();
    var tmpImg = new Image();
    tmpImg.src = self.imageUrl;
    tmpImg.onload = function() {
        self.showView();
    }
    tmpImg.onerror = function() {
        self.showView();
    }
  }
}

Вы можете использовать это так:

<div *appPreloadImage="'/url/of/preloaded/image.jpg'">
  <!-- Nothing in here will be displayed until the 
       request to the URL above has been completed 
      (even if that request fails) -->
</div>

(Обратите внимание на одинарные кавычки, вложенные в двойные кавычки - это потому, что мы передаем строковый литерал.)