HarmonyOS Web 加载完成后内容渐显 + 原生标题同步(实战模板)
一起来构建生态吧~
做 HarmonyOS 的 Web 组件(ArkWeb),我最在意的两个体验点其实很“用户视角”:
- 加载完成那一下,别突然“啪”地一跳(看起来像闪屏/卡顿)
- 原生标题栏要跟网页标题同步(不然一会儿显示 URL、一会儿空白,特别不专业)
这一篇我直接给你一套工程里能长期复用的模板:
Web 加载完成后 内容渐显(opacity 淡入)
原生标题 自动跟随网页 title,并且处理 onTitleReceive 返回 URL 的情况
背景知识:Web 组件支持
onTitleReceive通知“document 标题变化”。如果页面没设置 title,ArkWeb 可能在加载完成前基于 URL 生成标题返回给应用。官方也建议配合getTitle获取当前标题。:contentReference[oaicite:0]
一、整体思路(先说清楚)
1)内容渐显怎么做?
- Web 组件一直在(不要加载完才创建)
- 初始
opacity = 0 - 在
onPageEnd(或进度到一定值)时animateTo让opacity -> 1
2)标题同步怎么做?
标题我推荐走两条路兜底:
- 主路径:
onTitleReceive拿到标题就更新 - 兜底路径:
onPageEnd时runJavaScript('document.title')再取一次- 因为实践里有时
onTitleReceive可能先回 URL 或不稳定(尤其页面没写<title>时):contentReference[oaicite:1]
- 因为实践里有时
二、完整页面模板(可直接用)
你可以新建
WebFadeTitleSyncPage.ets,复制进去跑。
import { webview } from '@kit.ArkWeb';@Entry
@Component
struct WebFadeTitleSyncPage {private controller: webview.WebviewController = new webview.WebviewController();// 原生标题@State navTitle: string = '加载中…';// 渐显动画@State webOpacity: number = 0;@State isLoading: boolean = true;@State progress: number = 0;private fadeInWeb() {if (this.webOpacity >= 1) return;animateTo({ duration: 260, curve: Curve.EaseOut }, () => {this.webOpacity = 1;});}private updateTitleSafely(title?: string) {const t = (title ?? '').trim();// 过滤掉过短/空标题if (!t) return;// 有些场景 onTitleReceive 会先回 URL(尤其页面没设置 title)// 简单判断:像 URL 就先别抢占用户视线,等 onPageEnd 再兜底一次const looksLikeUrl =t.startsWith('http://') || t.startsWith('https://') || t.includes('://');if (looksLikeUrl && this.navTitle !== '加载中…') {return;}this.navTitle = t;}private async syncTitleFromDom() {// 用 JS 再取一次 document.title 做兜底(避免 onTitleReceive 返回 URL)try {this.controller.runJavaScript('document.title', (result: string) => {// result 的具体格式与实现有关:有的会带引号或是 JSON 字符串// 这里做一个简单清洗const cleaned = (result ?? '').replace(/^"+|"+$/g, '').trim();this.updateTitleSafely(cleaned);});} catch (e) {// 忽略:有些时机 Web 还没 ready 会失败}}build() {Column() {// ===== 原生标题栏(你也可以换成自定义导航组件)=====Row() {Text(this.navTitle).fontSize(18).fontWeight(FontWeight.Bold).maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })}.padding({ left: 16, right: 16, top: 12, bottom: 12 }).width('100%')// ===== Web 区域 =====Stack() {Web({src: 'https://example.com', // 换成你的 URL / rawfilecontroller: this.controller}).javaScriptAccess(true)// 页面开始加载:先把 Web 透明,标题设为“加载中”.onPageBegin(() => {this.isLoading = true;this.progress = 0;this.webOpacity = 0;this.navTitle = '加载中…';})// 进度:到一定程度可以提前淡入(体验更自然).onProgressChange((p: number) => {this.progress = p;if (p >= 80) {this.fadeInWeb();}})// 标题变化:优先用它同步标题// onTitleReceive:通知应用页面 document 标题已更改;如果页面没设置标题,可能返回 URL 形式:contentReference[oaicite:2]{index=2}.onTitleReceive((title: string) => {this.updateTitleSafely(title);})// 页面加载完成:兜底同步一次 title + 确保淡入.onPageEnd(() => {this.isLoading = false;this.fadeInWeb();this.syncTitleFromDom();}).opacity(this.webOpacity).width('100%').height('100%')// (可选)轻量 Loading 覆盖if (this.isLoading) {Column({ space: 8 }) {LoadingProgress()Text(`加载中 ${this.progress}%`).fontSize(12).opacity(0.65)}.width('100%').height('100%').justifyContent(FlexAlign.Center).backgroundColor('#FFFFFF').opacity(0.85)}}.width('100%').height('100%')}.width('100%').height('100%')}
}
三、为什么我建议“onTitleReceive + document.title 兜底”?
说人话就是:你别跟 Web 内核赌运气。
onTitleReceive很好用,标题一变就能收到- 但如果页面没设置
<title>,ArkWeb 有可能会在加载完成前用 URL 生成标题返回给你(你看起来就像“标题栏显示了一串网址”)华为开发者官网+1 - 所以
onPageEnd再runJavaScript('document.title')取一次,基本就稳了
四、我项目里常加的一点“人性化细节”
1)标题别疯狂抖动
有些网页会频繁改 title(比如路由切换、埋点)。你可以:
- 对 title 做 200ms 防抖再更新
- 或者只在“页面完成加载后”才允许更新
2)淡入不要太慢
我习惯 220~280ms,超过 400ms 会显得“粘”。