辽源市网站建设_网站建设公司_PHP_seo优化
2025/12/22 21:56:55 网站建设 项目流程

HarmonyOS Web 加载骨架屏 + Web 淡入动画模板(可直接用)

一起来构建生态吧~

在真实项目里,Web 页面加载体验好不好,不取决于页面最终长什么样,而取决于:
用户点进去后的前 1~3 秒你给了什么反馈

如果这段时间是白屏、卡住、没反应,哪怕页面最后加载得再漂亮,体验分也已经掉了。

这篇文章我给你一套可以直接复制进工程使用的方案,实现这几个目标:

  • 页面一进来 立刻显示原生骨架屏
  • Web 在后台加载,不抢视觉
  • Web 可展示时 淡入
  • 骨架屏 淡出
  • 全程无白屏、无闪烁、无突兀跳变

一、整体思路(先讲清楚,不然容易写歪)

一句话概括这套方案:

Web 始终存在,但一开始是透明的;
骨架屏覆盖在上面;
Web 准备好后淡入,骨架屏淡出。

关键点有 3 个:

  1. Web 不要等加载完才创建(否则必闪)
  2. 骨架屏是原生组件,不是 Web 里的 loading
  3. 动画只控制 opacity,不做 layout 变更

二、页面级完整模板(直接可用)

这是一个完整的 ArkTS 页面,你可以直接新建
WebSkeletonFadePage.ets 使用。

import { webview } from '@kit.ArkWeb';@Entry
@Component
struct WebSkeletonFadePage {private controller: webview.WebviewController = new webview.WebviewController();@State isSkeletonVisible: boolean = true;@State skeletonOpacity: number = 1;@State webOpacity: number = 0;@State progress: number = 0;@State showError: boolean = false;private fadeInWebAndHideSkeleton() {// 避免重复触发if (!this.isSkeletonVisible && this.webOpacity >= 1) return;// Web 淡入animateTo({ duration: 240, curve: Curve.EaseOut }, () => {this.webOpacity = 1;});// 骨架屏淡出animateTo({ duration: 220, curve: Curve.EaseIn }, () => {this.skeletonOpacity = 0;});// 动画结束后移除骨架屏setTimeout(() => {this.isSkeletonVisible = false;}, 240);}build() {Stack() {// Web 组件(始终存在,只是透明度变化)Web({src: 'https://example.com', // 换成你的地址或 rawfilecontroller: this.controller}).javaScriptAccess(true).onPageBegin(() => {this.showError = false;this.progress = 0;this.isSkeletonVisible = true;this.skeletonOpacity = 1;this.webOpacity = 0;}).onProgressChange((p: number) => {this.progress = p;// 经验值:80% 基本可见首屏if (p >= 80) {this.fadeInWebAndHideSkeleton();}}).onPageEnd(() => {// 兜底,确保一定淡入this.fadeInWebAndHideSkeleton();}).onRenderExited(() => {this.showError = true;this.isSkeletonVisible = false;this.webOpacity = 1;}).opacity(this.webOpacity).width('100%').height('100%')// 骨架屏if (this.isSkeletonVisible) {SkeletonLayer({progress: this.progress,opacity: this.skeletonOpacity})}// 错误态(可选)if (this.showError) {Column({ space: 12 }) {Text('加载失败').fontSize(18).fontWeight(FontWeight.Bold)Text('请检查网络后重试').opacity(0.7)Button('重试').onClick(() => {this.showError = false;this.isSkeletonVisible = true;this.skeletonOpacity = 1;this.webOpacity = 0;this.controller.refresh();})}.width('100%').height('100%').justifyContent(FlexAlign.Center)}}.width('100%').height('100%')}
}

三、骨架屏组件(轻量但“像样”)

1️⃣ 骨架屏整体结构

@Component
struct SkeletonLayer {progress: number;opacity: number;build() {Column({ space: 14 }) {SkeletonBlock({ w: '70%', h: 18, r: 8 })SkeletonBlock({ w: '40%', h: 14, r: 8 })Column({ space: 12 }) {ForEach([1, 2, 3, 4, 5], () => {Column({ space: 10 }) {SkeletonBlock({ w: '100%', h: 120, r: 12 })Row({ space: 10 }) {SkeletonBlock({ w: '22%', h: 14, r: 8 })SkeletonBlock({ w: '30%', h: 14, r: 8 })SkeletonBlock({ w: '18%', h: 14, r: 8 })}}})}Text(`加载中… ${this.progress}%`).fontSize(12).opacity(0.6)}.padding(16).width('100%').height('100%').backgroundColor('#FFFFFF').opacity(this.opacity)}
}

单个骨架块(带轻微流光)

@Component
struct SkeletonBlock {w: string;h: number;r: number;@State shimmerOffset: number = -60;aboutToAppear() {setInterval(() => {animateTo({ duration: 900, curve: Curve.Linear }, () => {this.shimmerOffset =this.shimmerOffset >= 260 ? -60 : this.shimmerOffset + 80;});}, 950);}build() {Stack() {Rect().width(this.w).height(this.h).radius(this.r).opacity(0.12)Rect().width('30%').height(this.h).radius(this.r).translate({ x: this.shimmerOffset }).opacity(0.08)}}
}

四、为什么这套方案“看起来就高级”

这是我在项目里反复打磨出来的结论:

  • 骨架屏是“页面结构的预告”,不是转圈圈
  • 淡入动画是心理缓冲,让内容出现得“理所当然”
  • Web 不重排、不重建,性能稳定
  • 动画只改 opacity,最安全、最不容易出问题

五、我踩过的坑,你可以直接避开

骨架屏用 Web 自己的 loading

→ Web 没加载前你根本看不到它,白屏依旧。

onPageEnd 才显示 Web

→ 页面其实早就能看了,被你硬生生挡住。

动画期间改布局

→ 闪、抖、性能下降,一堆莫名其妙的问题。


六、推荐的实际使用策略

  • 首屏 Web 页面:用这套方案
  • 页面内跳转 URL:只用淡入,不用骨架
  • 协议页 / 轻页面:直接 loading 即可

七、一句话总结

Web 页面加载不是“等它加载完”,
而是“在它加载的这段时间,你给用户什么感觉”。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询