如何在app-show-on-event组件及子组件视频海报加载完成前显示加载指示器?
实现视频海报加载完成前显示加载指示器的方案
这个需求很常见,尤其是当组件嵌套深、包含大量媒体资源时,提前展示加载态能避免用户看到空白或布局跳动。下面给你几个实用的实现思路,你可以根据项目结构选择:
方案一:在app-show-on-event组件内部管理加载状态
这种方案适合你有权限修改子组件代码的情况,逻辑更内聚:
1. 子组件添加加载状态输出
在app-show-on-event组件中,添加一个输出事件用来通知父组件所有海报已加载完成;同时在组件初始化后,遍历所有带poster的video元素,监听它们的海报加载状态:
import { Component, Output, EventEmitter, AfterViewInit, ElementRef } from '@angular/core'; @Component({ selector: 'app-show-on-event', templateUrl: './show-on-event.component.html', styleUrls: ['./show-on-event.component.css'] }) export class ShowOnEventComponent implements AfterViewInit { @Output() allPostersLoaded = new EventEmitter<boolean>(); private totalVideos = 0; private loadedCount = 0; constructor(private el: ElementRef) {} ngAfterViewInit() { this.trackVideoPosters(); } private trackVideoPosters() { // 获取所有设置了poster的video元素 const videos = this.el.nativeElement.querySelectorAll('video[poster]'); this.totalVideos = videos.length; // 如果没有视频,直接通知加载完成 if (this.totalVideos === 0) { this.allPostersLoaded.emit(true); return; } // 逐个监听海报加载 videos.forEach(video => { const posterImg = new Image(); posterImg.src = video.poster; // 处理已缓存的海报 if (posterImg.complete) { this.onPosterReady(); } else { posterImg.onload = () => this.onPosterReady(); // 海报加载失败也视为"就绪"(可根据需求调整为重试或错误提示) posterImg.onerror = () => this.onPosterReady(); } }); } private onPosterReady() { this.loadedCount++; // 所有海报加载完成后通知父组件 if (this.loadedCount === this.totalVideos) { this.allPostersLoaded.emit(true); } } }
2. 父组件控制加载态与组件显示
在父组件中,新增一个isLoading变量,初始为true;当触发显示事件时,重置加载状态,等待子组件的加载完成通知:
// 父组件.ts showThisComponent = false; isLoading = true; // 触发组件显示的事件处理函数 triggerComponentDisplay() { this.showThisComponent = true; this.isLoading = true; // 每次触发时重置加载状态 } // 接收子组件的加载完成通知 onAllPostersLoaded() { this.isLoading = false; }
父组件模板中,根据isLoading切换加载指示器和目标组件:
<!-- 父组件模板 --> <div *ngIf="showThisComponent"> <!-- 替换成你的加载指示器组件 --> <div class="loading-spinner" *ngIf="isLoading">加载中...</div> <app-show-on-event *ngIf="!isLoading" (allPostersLoaded)="onAllPostersLoaded()"> </app-show-on-event> </div>
方案二:用指令解耦(无需修改子组件)
如果你不想改动app-show-on-event的代码,可以用Angular指令来监听DOM中的视频海报加载状态,实现解耦:
1. 创建监听海报加载的指令
import { Directive, ElementRef, Output, EventEmitter, AfterViewInit } from '@angular/core'; @Directive({ selector: '[waitForVideoPosters]' }) export class WaitForVideoPostersDirective implements AfterViewInit { @Output() allPostersLoaded = new EventEmitter<boolean>(); private totalVideos = 0; private loadedCount = 0; constructor(private el: ElementRef) {} ngAfterViewInit() { this.trackAllPosters(); } private trackAllPosters() { const videos = this.el.nativeElement.querySelectorAll('video[poster]'); this.totalVideos = videos.length; if (this.totalVideos === 0) { this.allPostersLoaded.emit(true); return; } videos.forEach(video => this.monitorSinglePoster(video)); } private monitorSinglePoster(video: HTMLVideoElement) { const img = new Image(); img.src = video.poster; if (img.complete) { this.onPosterReady(); } else { img.onload = () => this.onPosterReady(); img.onerror = () => this.onPosterReady(); } } private onPosterReady() { this.loadedCount++; if (this.loadedCount === this.totalVideos) { this.allPostersLoaded.emit(true); } } }
2. 父组件中使用指令
父组件模板调整为:
<!-- 父组件模板 --> <div *ngIf="showThisComponent"> <div class="loading-spinner" *ngIf="isLoading">加载中...</div> <div *ngIf="!isLoading" waitForVideoPosters (allPostersLoaded)="isLoading = false"> <app-show-on-event></app-show-on-event> </div> </div>
父组件的TS代码和方案一一致,无需修改子组件即可实现需求。
补充:处理动态生成的视频
如果app-show-on-event中的视频是通过*ngFor或异步数据动态生成的,上面的方案可能会漏掉后期添加的视频。这时候可以用MutationObserver监听DOM变化,自动追踪新添加的视频:
在子组件或指令中添加以下代码(以方案一的子组件为例):
private observer: MutationObserver; ngAfterViewInit() { this.trackVideoPosters(); // 监听DOM子节点变化,处理动态添加的视频 this.observer = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { const newVideos = (node as Element).querySelectorAll('video[poster]'); newVideos.forEach(video => { this.totalVideos++; this.monitorSinglePoster(video); // 复用之前的监听方法 }); } }); }); }); this.observer.observe(this.el.nativeElement, { childList: true, subtree: true }); } ngOnDestroy() { // 组件销毁时断开监听,避免内存泄漏 if (this.observer) { this.observer.disconnect(); } } // 把之前的trackVideoPosters里的单个视频监听抽成方法 private monitorSinglePoster(video: HTMLVideoElement) { const posterImg = new Image(); posterImg.src = video.poster; if (posterImg.complete) { this.onPosterReady(); } else { posterImg.onload = () => this.onPosterReady(); posterImg.onerror = () => this.onPosterReady(); } }
这样就能覆盖动态添加的视频元素,确保所有海报都被追踪到。
内容的提问来源于stack exchange,提问作者sanjihan
相关产品推荐
相关产品推荐

