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>
(Обратите внимание на одинарные кавычки, вложенные в двойные кавычки - это потому, что мы передаем строковый литерал.)