Angular中通过检测DOM类实现ng-lazyload-image加载时显示骨架屏
在Angular中结合ng-lazyload-image与ngx-skeleton-loader实现图片懒加载+骨架屏
问题分析
你之前的方案存在两个核心问题:
- 模板表达式中无法直接访问DOM元素的
classList属性,Angular模板上下文不支持这类直接DOM操作。 - 使用
@ViewChild('productImage')只能获取第一个匹配的图片元素,导致第一张图片加载完成后,所有骨架屏都跟着隐藏,无法为每个图片维护独立的加载状态。
下面提供三种可行的解决方案,按推荐程度排序:
方案一:纯CSS控制(最简洁,无需TS逻辑)
利用ng-lazyload-image加载完成后自动添加的ng-lazyloaded类,通过CSS相邻兄弟选择器控制骨架屏的显示/隐藏:
模板代码
<div class="image-wrapper" *ngFor="let item of imageList"> <ngx-skeleton-loader class="image-cover" [theme]="{ height: '100%', 'margin-bottom': '0' }"></ngx-skeleton-loader> <img class="image-cover" [defaultImage]="defaultImage" [lazyLoad]="item.image" /> </div>
CSS代码
.image-wrapper { position: relative; width: 200px; /* 根据你的布局调整 */ height: 200px; } .image-cover { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; } /* 默认隐藏图片,显示骨架屏 */ img.image-cover { display: none; } /* 图片加载完成后显示图片,隐藏骨架屏 */ img.image-cover.ng-lazyloaded { display: block; } img.image-cover.ng-lazyloaded + ngx-skeleton-loader { display: none; }
方案二:利用ng-lazyload-image的加载事件(更可靠)
通过(load)事件监听图片加载完成,为每个图片维护独立的加载状态:
组件TS代码
import { Component } from '@angular/core'; interface ImageItem { image: string; isLoaded: boolean; } @Component({ selector: 'app-lazy-image-grid', templateUrl: './lazy-image-grid.component.html', styleUrls: ['./lazy-image-grid.component.css'] }) export class LazyImageGridComponent { defaultImage = 'path/to/default/placeholder.jpg'; imageList: ImageItem[] = [ { image: 'path/to/image1.jpg', isLoaded: false }, { image: 'path/to/image2.jpg', isLoaded: false }, // 更多图片项 ]; onImageLoad(index: number) { this.imageList[index].isLoaded = true; } }
模板代码
<div *ngFor="let item of imageList; let i = index" class="image-wrapper"> <!-- 骨架屏:未加载时显示 --> <ngx-skeleton-loader *ngIf="!item.isLoaded" class="image-cover" [theme]="{ height: '100%', 'margin-bottom': '0' }"> </ngx-skeleton-loader> <!-- 图片:加载完成后显示 --> <img *ngIf="item.isLoaded" class="image-cover" [defaultImage]="defaultImage" [lazyLoad]="item.image" (load)="onImageLoad(i)"> </div>
方案三:监听DOM类变化(依赖ng-lazyloaded类)
如果必须通过判断ng-lazyloaded类实现,可使用ViewChildren获取所有图片元素,结合MutationObserver监听类变化:
组件TS代码
import { Component, QueryList, ViewChildren, ElementRef, AfterViewInit } from '@angular/core'; interface ImageItem { image: string; isLoaded: boolean; } @Component({ selector: 'app-lazy-image-grid', templateUrl: './lazy-image-grid.component.html', styleUrls: ['./lazy-image-grid.component.css'] }) export class LazyImageGridComponent implements AfterViewInit { defaultImage = 'path/to/default/placeholder.jpg'; imageList: ImageItem[] = [ { image: 'path/to/image1.jpg', isLoaded: false }, { image: 'path/to/image2.jpg', isLoaded: false }, // 更多图片项 ]; @ViewChildren('lazyImg') lazyImages!: QueryList<ElementRef>; ngAfterViewInit() { this.lazyImages.forEach((imgEl, index) => { const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.attributeName === 'class' && imgEl.nativeElement.classList.contains('ng-lazyloaded')) { this.imageList[index].isLoaded = true; observer.disconnect(); // 监听一次即可 } }); }); observer.observe(imgEl.nativeElement, { attributes: true }); }); } }
模板代码
<div *ngFor="let item of imageList; let i = index" class="image-wrapper"> <ngx-skeleton-loader *ngIf="!item.isLoaded" class="image-cover" [theme]="{ height: '100%', 'margin-bottom': '0' }"> </ngx-skeleton-loader> <img #lazyImg class="image-cover" [defaultImage]="defaultImage" [lazyLoad]="item.image"> </div>
内容的提问来源于stack exchange,提问作者NiceToMytyuk
相关产品推荐
相关产品推荐

