我有这个角度项目,我有一个填充页面的大背景图像和一个带有链接的简单侧边栏,点击后,将使用另一个图像(来自cdn)更改背景的URL。由于这些图像相当大,它们需要一两秒才能加载并且它很明显,我想添加预加载器,但我不确定如何在角度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';
}
因此,网址实际上已立即更改,但图片需要加载一些。我想添加一个加载gif或类似的东西,以保持图像更改为用户平滑,而不是像现在一样跳跃。
答案 0 :(得分:14)
一种方法是使用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());
});
}
}
NB :您应该键入数据参数,键入是确保代码一致性的好方法,any
只应该用作小丑,例如Object
在Java
。
答案 1 :(得分:8)
此解决方案利用了角度和浏览器已经提供的角度。图像加载由浏览器完成,无需自己处理任何数据或DOM。
我已经在Chrome 53上对此进行了测试,并且它运行良好。
这是你的元素,它的背景发生了变化:
<div class="yourBackgroundClass" [style.background-image]="'url(' + imgUrl + ')'"></div>
要预取图像,我们使用未显示的图像标记。最好另外制作position: absolute
并将其移出视图或使其非常小,这样它就不会干扰您的实际内容。
<img [src]="imgPreloadUrl" (load)="imgUrl = imgPreloadUrl" hidden>
通过设置imgPreloadUrl
,img
&#39; src
会按角度更新,浏览器会将图片加载到不可见的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>
答案 2 :(得分:7)
使用图片对象(Plunker Demo ⇗)
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';
}
答案 3 :(得分:1)
您需要的东西很少,例如http,解析器和消毒剂。这是link的解释,它是如何从头开始实现的。
例如,您有一个请求,然后将返回的Blob转换为安全样式,以便我们能够在样式指令中使用它
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);
})
)
答案 4 :(得分:0)
我最近将其实现为structural directive:
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>
(请注意,单引号嵌套在双引号内-这是因为我们要传递字符串文字。)