现象:大厅资源下载完成、splash 进度满后,进入 H5 前先闪一下白屏、又重新 显示一次 splash 背景图,才进 H5(真机 nova 14 复现)。 根因:原启动图视觉随 SplashPage 渲染,replacePathByName 切到 BridgeGameContainer 时,旧页销毁与新页 Web 首帧之间存在导航空隙(白闪); 而 BridgeGameContainer 自带的大厅 cover 又在切换后重新挂载,看起来像"第二次 splash"。两者皆因视觉绑定在会被切换/重建的页面上、与时序耦合。 修法(H5 零改动,纯原生): - 启动图 + 金色进度条整体上移到 Index 根层 Stack 覆盖层,由 AppStorage KEY_SPLASH_VISIBLE 控制,从 app 启动持续显示到大厅首帧上屏(onPageEnd)才撤。 - SplashPage 退化为纯启动逻辑载体(透明 NavDestination),仅把进度/文案/ 阻断写入 AppStorage 供根层覆盖层呈现。 - BridgeGameContainer 移除自带大厅 cover,onPageEnd 置 splashVisible=false。 SplashPage→BridgeGameContainer 的页面切换由此发生在常驻覆盖层之下,时序无关, 彻底消除导航空隙白闪与二次 splash,大厅就绪即直接显示 H5。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
75 lines
3.1 KiB
Plaintext
75 lines
3.1 KiB
Plaintext
import { common } from '@kit.AbilityKit';
|
|
import { StartupOrchestrator, StartupResult, StartupStage } from 'domain_resource';
|
|
import { RouteName, BridgeGameParams } from '../routes/AppRoutes';
|
|
import { KEY_SPLASH_VISIBLE, KEY_SPLASH_PERCENT, KEY_SPLASH_LABEL, KEY_SPLASH_BLOCKED } from './Index';
|
|
|
|
/**
|
|
* 启动引导页(对应 weclomeactivity1,框架 §4/§8.1)——**纯启动逻辑载体,无可见 UI**。
|
|
* 进入即跑 StartupOrchestrator(本地配置→远程配置→资源准备→app_data 注入),进度/文案/阻断写入
|
|
* AppStorage,由 Index 根层启动页覆盖层统一呈现;完成后 replace 进大厅容器(覆盖层在大厅首帧后撤)。
|
|
*
|
|
* 视觉放在 Index 根层而非本页:使 splash→大厅的页面切换发生在覆盖层之下,彻底消除导航空隙白闪。
|
|
*/
|
|
@Component
|
|
export struct SplashPage {
|
|
pathStack: NavPathStack = new NavPathStack();
|
|
|
|
aboutToAppear(): void {
|
|
this.runStartup();
|
|
}
|
|
|
|
private async runStartup(): Promise<void> {
|
|
const ctx: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
|
|
const orchestrator = new StartupOrchestrator(ctx, (stage: StartupStage, percent: number) => {
|
|
AppStorage.setOrCreate(KEY_SPLASH_PERCENT, percent);
|
|
AppStorage.setOrCreate(KEY_SPLASH_LABEL, SplashPage.stageLabel(stage, percent));
|
|
});
|
|
try {
|
|
const result: StartupResult = await orchestrator.run();
|
|
if (result.enter) {
|
|
// 切到大厅:保持 splashVisible=true,根层覆盖层持续盖住,由大厅首帧 onPageEnd 撤除。
|
|
const params: BridgeGameParams = { entryUrl: result.entryUrl, launchType: '0' };
|
|
this.pathStack.replacePathByName(RouteName.BRIDGE_GAME, params, false);
|
|
} else {
|
|
AppStorage.setOrCreate(KEY_SPLASH_BLOCKED, result.message);
|
|
}
|
|
} catch (e) {
|
|
const err = e as Error;
|
|
AppStorage.setOrCreate(KEY_SPLASH_BLOCKED, `启动失败:${err.message}`);
|
|
}
|
|
}
|
|
|
|
/** 阶段 → 用户可读中文文案(PREPARE_RESOURCE 按进度区分"准备资源/下载更新")。 */
|
|
private static stageLabel(stage: StartupStage, percent: number): string {
|
|
switch (stage) {
|
|
case StartupStage.INIT:
|
|
return '正在启动…';
|
|
case StartupStage.LOAD_LOCAL_CONFIG:
|
|
return '读取本地配置…';
|
|
case StartupStage.REQUEST_PERMISSIONS:
|
|
return '检测网络权限…';
|
|
case StartupStage.FETCH_REMOTE_CONFIG:
|
|
return '获取配置文件…';
|
|
case StartupStage.RESOLVE_VERSION:
|
|
return '检测升级版本…';
|
|
case StartupStage.PREPARE_RESOURCE:
|
|
// 35% 为内置资源准备;55%~85% 为远程 zip 下载
|
|
return percent >= 55 ? '正在下载更新…' : '准备游戏资源…';
|
|
case StartupStage.INJECT_APP_DATA:
|
|
return '即将进入大厅…';
|
|
case StartupStage.ENTER_HALL:
|
|
return '进入大厅';
|
|
default:
|
|
return '正在启动…';
|
|
}
|
|
}
|
|
|
|
build() {
|
|
// 视觉由 Index 根层启动页覆盖层呈现;本页仅承载启动逻辑(透明、无内容)
|
|
NavDestination() {
|
|
}
|
|
.hideTitleBar(true)
|
|
.backgroundColor(Color.Transparent)
|
|
}
|
|
}
|