HarmonyOS Web 加载完成后内容渐显 + 原生标题同步(实战模板)
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 / rawfile
controller: 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 会显得“粘”。
五、结尾(可以直接当博客收尾)
Web 的“加载完成”不是一个事件,而是一段体验。
用渐显把那一下“硬切”磨平,再把标题同步好,
你的 Web 页面看起来就会从“嵌了一块网页”变成“这个 App 的一部分”。

浙公网安备 33010602011771号