Angular 5中如何实现子组件图片加载完成前的全屏启动屏?
这个问题我之前在Angular项目里也遇到过,window.onload失效的核心原因是Angular的组件渲染时机和浏览器的window.onload触发时机不匹配——当window.onload触发时,你的子组件可能还没完成DOM渲染,那些高清图片甚至还没被插入到页面中,自然没法监听它们的加载状态。下面给你一套可行的实现方案:
1. 先搞定启动屏的基础样式
首先给你的启动屏写全屏覆盖的CSS,确保它在所有内容之上,还能加个友好的过渡效果:
#splash { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: #ffffff; /* 可替换成你需要的背景色 */ display: flex; align-items: center; justify-content: center; z-index: 9999; /* 确保在所有页面内容上层 */ transition: opacity 0.3s ease-in-out; /* 淡出动画提升体验 */ } #splash.hidden { opacity: 0; pointer-events: none; /* 隐藏后不阻挡页面元素的点击交互 */ }
2. 组件内监听图片加载状态(核心实现)
Angular中我们需要在组件视图完全初始化后去监听图片,也就是用ngAfterViewInit钩子。这里分两种场景处理:
场景一:图片是静态写在模板里的
如果你的子组件图片是固定在模板中的,直接在父组件(page.component)里做如下操作:
page.component.html
<div id="page" class="page"> <div id="splash" [class.hidden]="isSplashHidden"></div> <div id="home-page"> <!-- 你的子组件和图片内容 --> <app-child-component></app-child-component> </div> </div>
page.component.ts
import { Component, AfterViewInit, ElementRef } from '@angular/core'; @Component({ selector: 'app-page', templateUrl: './page.component.html', styleUrls: ['./page.component.css'] }) export class PageComponent implements AfterViewInit { isSplashHidden = false; constructor(private elRef: ElementRef) {} ngAfterViewInit(): void { // 获取页面中所有img元素(包括子组件内的) const allImages = this.elRef.nativeElement.querySelectorAll('img'); if (allImages.length === 0) { // 没有图片直接隐藏启动屏 this.isSplashHidden = true; return; } let loadedCount = 0; const totalImages = allImages.length; // 遍历每个图片,绑定加载监听 allImages.forEach((img: HTMLImageElement) => { // 处理已缓存完成的图片 if (img.complete) { loadedCount++; this.checkIfAllLoaded(loadedCount, totalImages); return; } // 监听加载成功事件 img.addEventListener('load', () => { loadedCount++; this.checkIfAllLoaded(loadedCount, totalImages); }); // 监听加载失败事件(防止单张图片加载失败导致启动屏一直显示) img.addEventListener('error', () => { loadedCount++; this.checkIfAllLoaded(loadedCount, totalImages); }); }); } private checkIfAllLoaded(loaded: number, total: number): void { if (loaded === total) { // 所有图片加载完成,加个小延迟让过渡更自然 setTimeout(() => { this.isSplashHidden = true; }, 300); } } }
场景二:图片是动态渲染的(比如从API获取URL)
如果你的图片是通过异步数据渲染的(比如子组件用*ngFor循环加载图片URL),ngAfterViewInit执行时DOM里还没有这些图片,这时候需要用MutationObserver监听DOM变化,当图片元素被插入后再绑定监听:
修改page.component.ts的ngAfterViewInit方法:
ngAfterViewInit(): void { const targetNode = this.elRef.nativeElement.querySelector('#home-page'); const observerConfig = { childList: true, subtree: true }; const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { if (mutation.addedNodes.length > 0) { // 筛选新增节点中的img元素 const newImages = Array.from(mutation.addedNodes) .filter(node => node.nodeName === 'IMG') as HTMLImageElement[]; if (newImages.length > 0) { this.setupImageListeners(newImages); } } }); }); observer.observe(targetNode, observerConfig); // 先检查当前已存在的图片 const existingImages = targetNode.querySelectorAll('img'); this.setupImageListeners(Array.from(existingImages)); } private setupImageListeners(images: HTMLImageElement[]): void { let loadedCount = 0; const totalImages = images.length; images.forEach(img => { if (img.complete) { loadedCount++; this.checkIfAllLoaded(loadedCount, totalImages); return; } img.addEventListener('load', () => { loadedCount++; this.checkIfAllLoaded(loadedCount, totalImages); }); img.addEventListener('error', () => { loadedCount++; this.checkIfAllLoaded(loadedCount, totalImages); }); }); } // 保留之前的checkIfAllLoaded方法
3. 为什么window.onload不好用?
再明确下你遇到的问题:window.onload是当整个文档(包括所有外部资源:图片、脚本、样式表等)加载完成后触发,但Angular的组件渲染是在window.onload之后才开始的——Angular会先加载自身框架代码,再编译组件、渲染DOM。这就导致window.onload触发时,子组件里的高清图片还没被插入到DOM中,自然无法捕获它们的加载事件,所以这个方法对你的场景无效。
额外优化建议
- 给图片添加懒加载逻辑(即便要等所有图片加载完再隐藏启动屏,懒加载也能优化初始加载压力);
- 对高清图片进行压缩,或者提供WebP格式的图片,减少加载时长;
- 在启动屏中添加加载动画(比如旋转的Spinner),提升用户等待时的体验。
内容的提问来源于stack exchange,提问作者25HRAF

