在加载 <IMG> 标签中的所有图像之前,不应加载 ngOnInit 或 ngAfterViewInit 中的内容
Content in ngOnInit or ngAfterViewInit should not load until alll images in <IMG> tags are loaded
我正在使用 Angular 8 作为基于博客的网络应用程序。数据暂时存储在 json 文件中,甚至图像也将随路径一起加载。
JSON数据
[
{
"imgSrc": "./assets/images/dalai-hills-1.jpg",
"destination": "Dalai Hills",
"introTitle": "through happy valley, to a picturesque place",
"place": "mussoorie, uttarakhand",
"description": "Lorem ipsum dolor sit amet, no elitr tation delicata cum, mei in causae deseruisse.",
}
]
imgSrc 决定加载哪个图像。所有图像都已经优化并放置在资产文件夹中。
模板
<article class="blog-card" style="border-top: 0;" *ngFor="let blog of blogsList">
<div class="blog-img-wrap" style="min-height: 200px;">
<a href="#"">
<img loading="lazy" class="img-fluid blog-img" src="{{ blog.imgSrc }}" alt="blog-image-1">
</a>
</div>
</article>
比方说,在博客页面中,由于 ,加载时有 12 张图片正在加载,我想确保页面只在所有图片都加载后才加载。
我没有得到关于 Whosebug 的任何具体答案。目前,加载的文本和图像之间存在非常小的秒差,但看起来很奇怪。
有相同的解决方案吗?
P.S:我想避免jQuery。
可以;
- 以编程方式创建图像元素。 (使用 HTMLImageElement)
- 跟踪他们的加载状态。 (使用 ReplaySubject 和 forkJoin)
- 加载完所有图像后,在页面中显示它们。 (使用 异步管道 和 Renderer2)
这是一个示例实现(解释在注释中);
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
data = [
{
imgSrc: "https://img.jakpost.net/c/2017/10/27/2017_10_27_34794_1509067747._large.jpg",
destination: "destination-01",
introTitle: "introTitle-01",
place: "place-01",
description: "description-01"
},
{
imgSrc: "https://lakecomofoodtours.com/wp-content/uploads/gravedona-celiaa-img-0282_orig-800x600.jpg",
destination: "destination-02",
introTitle: "introTitle-02",
place: "place-02",
description: "description-02"
},
{
imgSrc: "https://italicsmag.com/wp-content/uploads/2020/05/Lake-Como-5-770x550.jpg",
destination: "destination-03",
introTitle: "introTitle-03",
place: "place-03",
description: "description-03"
}
];
/* This array holds Observables for images' loading status */
private tmp: ReplaySubject<Event>[] = [];
blogData: BlogDataType[] = this.data.map(d => {
const img = new Image();
img.height = 200;
img.width = 300;
const evt = new ReplaySubject<Event>(1);
img.onload = e => {
evt.next(e);
evt.complete();
};
this.tmp.push(evt);
img.src = d.imgSrc
return { ...d, imgElem: img };
});
/*
* Convert images' loading status observables to a higher-order observable .
* When all observables complete, forkJoin emits the last emitted value from each.
* since we emit only one value and complete in img.load callback, forkJoin suits our needs.
*
* when forkJoin emits (i.e. all images are loaded) we emit this.blogData so that we use it
* in template with async pipe
*/
blogsList: Observable<BlogDataType[]> = forkJoin(this.tmp).pipe(
map(() => this.blogData)
);
constructor(private renderer: Renderer2) {}
/* manually append image elements to DOM, used in template */
appendImg(anchor: HTMLAnchorElement, img: HTMLImageElement) {
this.renderer.appendChild(anchor, img);
return "";
}
}
interface BlogDataType {
imgElem: HTMLImageElement;
imgSrc: string;
destination: string;
introTitle: string;
place: string;
description: string;
}
<div *ngFor="let blog of blogsList | async">
<div>{{blog.description}}</div>
<a #aEl href="#">
{{ appendImg(aEl, blog.imgElem) }}
</a>
</div>
这是一个工作演示:https://stackblitz.com/edit/angular-ivy-ymmfz6
请注意,此实现不是防错的。 img.onerror
也应该处理以用于生产,为了简单起见,我跳过了它。
我正在使用 Angular 8 作为基于博客的网络应用程序。数据暂时存储在 json 文件中,甚至图像也将随路径一起加载。
JSON数据
[
{
"imgSrc": "./assets/images/dalai-hills-1.jpg",
"destination": "Dalai Hills",
"introTitle": "through happy valley, to a picturesque place",
"place": "mussoorie, uttarakhand",
"description": "Lorem ipsum dolor sit amet, no elitr tation delicata cum, mei in causae deseruisse.",
}
]
imgSrc 决定加载哪个图像。所有图像都已经优化并放置在资产文件夹中。
模板
<article class="blog-card" style="border-top: 0;" *ngFor="let blog of blogsList">
<div class="blog-img-wrap" style="min-height: 200px;">
<a href="#"">
<img loading="lazy" class="img-fluid blog-img" src="{{ blog.imgSrc }}" alt="blog-image-1">
</a>
</div>
</article>
比方说,在博客页面中,由于 ,加载时有 12 张图片正在加载,我想确保页面只在所有图片都加载后才加载。
我没有得到关于 Whosebug 的任何具体答案。目前,加载的文本和图像之间存在非常小的秒差,但看起来很奇怪。
有相同的解决方案吗?
P.S:我想避免jQuery。
可以;
- 以编程方式创建图像元素。 (使用 HTMLImageElement)
- 跟踪他们的加载状态。 (使用 ReplaySubject 和 forkJoin)
- 加载完所有图像后,在页面中显示它们。 (使用 异步管道 和 Renderer2)
这是一个示例实现(解释在注释中);
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
data = [
{
imgSrc: "https://img.jakpost.net/c/2017/10/27/2017_10_27_34794_1509067747._large.jpg",
destination: "destination-01",
introTitle: "introTitle-01",
place: "place-01",
description: "description-01"
},
{
imgSrc: "https://lakecomofoodtours.com/wp-content/uploads/gravedona-celiaa-img-0282_orig-800x600.jpg",
destination: "destination-02",
introTitle: "introTitle-02",
place: "place-02",
description: "description-02"
},
{
imgSrc: "https://italicsmag.com/wp-content/uploads/2020/05/Lake-Como-5-770x550.jpg",
destination: "destination-03",
introTitle: "introTitle-03",
place: "place-03",
description: "description-03"
}
];
/* This array holds Observables for images' loading status */
private tmp: ReplaySubject<Event>[] = [];
blogData: BlogDataType[] = this.data.map(d => {
const img = new Image();
img.height = 200;
img.width = 300;
const evt = new ReplaySubject<Event>(1);
img.onload = e => {
evt.next(e);
evt.complete();
};
this.tmp.push(evt);
img.src = d.imgSrc
return { ...d, imgElem: img };
});
/*
* Convert images' loading status observables to a higher-order observable .
* When all observables complete, forkJoin emits the last emitted value from each.
* since we emit only one value and complete in img.load callback, forkJoin suits our needs.
*
* when forkJoin emits (i.e. all images are loaded) we emit this.blogData so that we use it
* in template with async pipe
*/
blogsList: Observable<BlogDataType[]> = forkJoin(this.tmp).pipe(
map(() => this.blogData)
);
constructor(private renderer: Renderer2) {}
/* manually append image elements to DOM, used in template */
appendImg(anchor: HTMLAnchorElement, img: HTMLImageElement) {
this.renderer.appendChild(anchor, img);
return "";
}
}
interface BlogDataType {
imgElem: HTMLImageElement;
imgSrc: string;
destination: string;
introTitle: string;
place: string;
description: string;
}
<div *ngFor="let blog of blogsList | async">
<div>{{blog.description}}</div>
<a #aEl href="#">
{{ appendImg(aEl, blog.imgElem) }}
</a>
</div>
这是一个工作演示:https://stackblitz.com/edit/angular-ivy-ymmfz6
请注意,此实现不是防错的。 img.onerror
也应该处理以用于生产,为了简单起见,我跳过了它。