修复大厅启动页→H5"白闪+二次显示 splash":启动图覆盖层上移到根层
现象:大厅资源下载完成、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>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
0425123465
commit
db206068c0
@@ -10,6 +10,7 @@ import { AppEnv, EventBus, EventPayload, NavEvents, OpenGenericWebPayload, Switc
|
|||||||
import { BridgeGameParams, GenericWebParams, GenericWebResult, RouteName, AppEvents } from '../routes/AppRoutes';
|
import { BridgeGameParams, GenericWebParams, GenericWebResult, RouteName, AppEvents } from '../routes/AppRoutes';
|
||||||
import { WebSlot } from '../web/WebSlot';
|
import { WebSlot } from '../web/WebSlot';
|
||||||
import { SharePanel } from '../components/SharePanel';
|
import { SharePanel } from '../components/SharePanel';
|
||||||
|
import { KEY_SPLASH_VISIBLE } from './Index';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 大厅/子游戏容器(对应 webviewActivity,框架 §7.1/§7.5)。**双 Web 槽模型**:
|
* 大厅/子游戏容器(对应 webviewActivity,框架 §7.1/§7.5)。**双 Web 槽模型**:
|
||||||
@@ -35,12 +36,12 @@ export struct BridgeGameContainer {
|
|||||||
private cancels: Array<() => void> = [];
|
private cancels: Array<() => void> = [];
|
||||||
/** 非空 → 子游戏 Web 显示(路径 A 切换)。 */
|
/** 非空 → 子游戏 Web 显示(路径 A 切换)。 */
|
||||||
@State private subgameUrl: string = '';
|
@State private subgameUrl: string = '';
|
||||||
/** 大厅首帧是否已绘制:未绘制时用启动图盖住大厅 Web,消除 splash→大厅的白屏闪烁。 */
|
|
||||||
@State private hallPainted: boolean = false;
|
|
||||||
/** 子游戏资源下载中(按需下载期间显示启动页同款进度条)。 */
|
/** 子游戏资源下载中(按需下载期间显示启动页同款进度条)。 */
|
||||||
@State private subgameDownloading: boolean = false;
|
@State private subgameDownloading: boolean = false;
|
||||||
/** 子游戏下载进度(0~100)。 */
|
/** 子游戏下载进度(0~100)。 */
|
||||||
@State private subgamePercent: number = 0;
|
@State private subgamePercent: number = 0;
|
||||||
|
/** 子游戏首帧是否已绘制:下载完到子游戏 H5 上屏间用启动图盖住,消除白屏。 */
|
||||||
|
@State private subgamePainted: boolean = false;
|
||||||
/** 远程配置 10 分钟缓存(对齐 Android:避免每次进子游戏都重拉 config)。 */
|
/** 远程配置 10 分钟缓存(对齐 Android:避免每次进子游戏都重拉 config)。 */
|
||||||
private cachedRemote: RemoteConfig | undefined = undefined;
|
private cachedRemote: RemoteConfig | undefined = undefined;
|
||||||
private cachedRemoteAt: number = 0;
|
private cachedRemoteAt: number = 0;
|
||||||
@@ -187,6 +188,7 @@ export struct BridgeGameContainer {
|
|||||||
this.slotL?.deactivate();
|
this.slotL?.deactivate();
|
||||||
this.slotS = new WebSlot('subgame', new webview.WebviewController(), true, this.uploadServer);
|
this.slotS = new WebSlot('subgame', new webview.WebviewController(), true, this.uploadServer);
|
||||||
this.applyOrientation(d.webtype !== '2'); // "3" 横 / "2" 竖
|
this.applyOrientation(d.webtype !== '2'); // "3" 横 / "2" 竖
|
||||||
|
this.subgamePainted = false; // 子游戏首帧前用启动图盖住,避免 下载完→子游戏上屏 间白屏
|
||||||
this.subgameUrl = `file://${subDir}/index.html?Launchtype=1`; // 触发子游戏 Web 渲染 → setupSubgame
|
this.subgameUrl = `file://${subDir}/index.html?Launchtype=1`; // 触发子游戏 Web 渲染 → setupSubgame
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,9 +319,8 @@ export struct BridgeGameContainer {
|
|||||||
this.slotL !== undefined ? this.slotL.onLoadIntercept(event.data.getRequestUrl()) : false)
|
this.slotL !== undefined ? this.slotL.onLoadIntercept(event.data.getRequestUrl()) : false)
|
||||||
.onPageEnd(() => {
|
.onPageEnd(() => {
|
||||||
this.slotL?.onPageEnd();
|
this.slotL?.onPageEnd();
|
||||||
// 页面加载完成(已稳定上屏)再撤启动图覆盖层——不在 onFirstContentfulPaint 撤,
|
// 大厅页面加载完成(已稳定上屏)→ 撤根层启动页覆盖,直接显示大厅 H5。
|
||||||
// 避免首帧绘制到合成上屏间一帧黑屏。
|
AppStorage.setOrCreate(KEY_SPLASH_VISIBLE, false);
|
||||||
this.hallPainted = true;
|
|
||||||
})
|
})
|
||||||
.onProgressChange((event: OnProgressChangeEvent) => {
|
.onProgressChange((event: OnProgressChangeEvent) => {
|
||||||
if (event.newProgress === 100) {
|
if (event.newProgress === 100) {
|
||||||
@@ -339,7 +340,10 @@ export struct BridgeGameContainer {
|
|||||||
.onControllerAttached(() => this.setupSubgame())
|
.onControllerAttached(() => this.setupSubgame())
|
||||||
.onLoadIntercept((event: OnLoadInterceptEvent) =>
|
.onLoadIntercept((event: OnLoadInterceptEvent) =>
|
||||||
this.slotS !== undefined ? this.slotS.onLoadIntercept(event.data.getRequestUrl()) : false)
|
this.slotS !== undefined ? this.slotS.onLoadIntercept(event.data.getRequestUrl()) : false)
|
||||||
.onPageEnd(() => this.slotS?.onPageEnd())
|
.onPageEnd(() => {
|
||||||
|
this.slotS?.onPageEnd();
|
||||||
|
this.subgamePainted = true; // 子游戏页面加载完、稳定上屏 → 撤启动图覆盖层
|
||||||
|
})
|
||||||
.onProgressChange((event: OnProgressChangeEvent) => {
|
.onProgressChange((event: OnProgressChangeEvent) => {
|
||||||
if (event.newProgress === 100) {
|
if (event.newProgress === 100) {
|
||||||
this.slotS?.onFirstProgress();
|
this.slotS?.onFirstProgress();
|
||||||
@@ -348,29 +352,33 @@ export struct BridgeGameContainer {
|
|||||||
.onRenderExited((event: OnRenderExitedEvent) => this.slotS?.onRenderExited(event.renderExitReason))
|
.onRenderExited((event: OnRenderExitedEvent) => this.slotS?.onRenderExited(event.renderExitReason))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 大厅首帧前用启动图盖住(消除 splash→大厅白屏);仅大厅阶段、首帧后撤除
|
// (大厅首帧前的启动图覆盖已上移到 Index.ets 根层,避免导航空隙白闪 + 二次 splash)
|
||||||
if (!this.hallPainted && this.subgameUrl === '') {
|
|
||||||
|
// 子游戏进入覆盖层(与启动页 SplashPage 完全一致):覆盖「下载中」与「下载完→子游戏首帧上屏」
|
||||||
|
// 两段,启动图铺底 + 底部金色进度条 + 文案,直到子游戏 onPageEnd 才撤——消除两段间白屏。
|
||||||
|
if (this.subgameDownloading || (this.subgameUrl !== '' && !this.subgamePainted)) {
|
||||||
|
Stack() {
|
||||||
Image($r('app.media.launch_image'))
|
Image($r('app.media.launch_image'))
|
||||||
.width('100%').height('100%')
|
.width('100%').height('100%')
|
||||||
.objectFit(ImageFit.Cover)
|
.objectFit(ImageFit.Cover)
|
||||||
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
|
|
||||||
}
|
|
||||||
|
|
||||||
// 子游戏按需下载进度(启动页同款:金色进度条 + 文案,覆盖全屏白底)
|
|
||||||
if (this.subgameDownloading) {
|
|
||||||
Column({ space: 14 }) {
|
Column({ space: 14 }) {
|
||||||
Progress({ value: this.subgamePercent, total: 100, type: ProgressType.Linear })
|
Progress({ value: this.subgameDownloading ? this.subgamePercent : 100, total: 100, type: ProgressType.Linear })
|
||||||
.width('56%')
|
.width('56%')
|
||||||
.color('#D2A312')
|
.color('#D2A312')
|
||||||
.backgroundColor('#E6E6E6')
|
.backgroundColor('#E6E6E6')
|
||||||
Row({ space: 8 }) {
|
Row({ space: 8 }) {
|
||||||
LoadingProgress().width(20).height(20).color('#D2A312')
|
LoadingProgress().width(20).height(20).color('#D2A312')
|
||||||
Text(`正在下载游戏… ${this.subgamePercent}%`).fontSize(15).fontColor('#1B2A4A')
|
Text(this.subgameDownloading ? `正在下载游戏… ${this.subgamePercent}%` : '正在进入游戏…')
|
||||||
|
.fontSize(15).fontColor('#1B2A4A')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.width('100%').height('100%')
|
.width('100%').height('100%')
|
||||||
.justifyContent(FlexAlign.Center)
|
.justifyContent(FlexAlign.End)
|
||||||
|
.padding({ bottom: 24 })
|
||||||
|
}
|
||||||
|
.width('100%').height('100%')
|
||||||
.backgroundColor(Color.White)
|
.backgroundColor(Color.White)
|
||||||
|
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分享面板(顶层叠加;用户选平台/取消 → 一次性回投 ShareProvider)
|
// 分享面板(顶层叠加;用户选平台/取消 → 一次性回投 ShareProvider)
|
||||||
|
|||||||
@@ -3,16 +3,37 @@ import { SplashPage } from './SplashPage';
|
|||||||
import { BridgeGameContainer } from './BridgeGameContainer';
|
import { BridgeGameContainer } from './BridgeGameContainer';
|
||||||
import { GenericWebContainer } from './GenericWebContainer';
|
import { GenericWebContainer } from './GenericWebContainer';
|
||||||
|
|
||||||
|
/** 根层启动页状态(AppStorage 键)。SplashPage 更新进度/文案/阻断;大厅首帧后置 splashVisible=false。 */
|
||||||
|
export const KEY_SPLASH_VISIBLE: string = 'splashVisible';
|
||||||
|
export const KEY_SPLASH_PERCENT: string = 'splashPercent';
|
||||||
|
export const KEY_SPLASH_LABEL: string = 'splashLabel';
|
||||||
|
export const KEY_SPLASH_BLOCKED: string = 'splashBlocked';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用导航宿主(@Entry)。持有全局 NavPathStack,初始进入 Splash。
|
* 应用导航宿主(@Entry)。持有全局 NavPathStack,初始进入 Splash。
|
||||||
* 各容器作为 NavDestination 子页,经 navDestination builder 按路由名分发。
|
*
|
||||||
|
* **根层启动页覆盖**:启动页视觉(启动图 + 进度条)在此根层渲染,从 app 启动**持续显示**到大厅
|
||||||
|
* 首帧上屏才撤。SplashPage→BridgeGameContainer 的页面切换发生在该覆盖层**之下**——彻底消除
|
||||||
|
* 导航空隙白闪与"二次显示 splash",大厅就绪即直接显示 H5。SplashPage 退化为纯启动逻辑载体。
|
||||||
*/
|
*/
|
||||||
@Entry
|
@Entry
|
||||||
@Component
|
@Component
|
||||||
struct Index {
|
struct Index {
|
||||||
private pathStack: NavPathStack = new NavPathStack();
|
private pathStack: NavPathStack = new NavPathStack();
|
||||||
|
@StorageProp(KEY_SPLASH_VISIBLE) private splashVisible: boolean = true;
|
||||||
|
@StorageProp(KEY_SPLASH_PERCENT) private splashPercent: number = 0;
|
||||||
|
@StorageProp(KEY_SPLASH_LABEL) private splashLabel: string = '正在启动…';
|
||||||
|
@StorageProp(KEY_SPLASH_BLOCKED) private splashBlocked: string = '';
|
||||||
|
|
||||||
|
// 品牌色(取自 logo):navy 文字 + 金色进度
|
||||||
|
private static readonly NAVY: string = '#1B2A4A';
|
||||||
|
private static readonly GOLD: string = '#D2A312';
|
||||||
|
|
||||||
aboutToAppear(): void {
|
aboutToAppear(): void {
|
||||||
|
AppStorage.setOrCreate(KEY_SPLASH_VISIBLE, true);
|
||||||
|
AppStorage.setOrCreate(KEY_SPLASH_PERCENT, 0);
|
||||||
|
AppStorage.setOrCreate(KEY_SPLASH_LABEL, '正在启动…');
|
||||||
|
AppStorage.setOrCreate(KEY_SPLASH_BLOCKED, '');
|
||||||
// 启动即进入引导页(对应 weclomeactivity1 为 Launcher)
|
// 启动即进入引导页(对应 weclomeactivity1 为 Launcher)
|
||||||
this.pathStack.pushPathByName(RouteName.SPLASH, '', false);
|
this.pathStack.pushPathByName(RouteName.SPLASH, '', false);
|
||||||
}
|
}
|
||||||
@@ -31,10 +52,45 @@ struct Index {
|
|||||||
}
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
|
Stack() {
|
||||||
Navigation(this.pathStack) {
|
Navigation(this.pathStack) {
|
||||||
}
|
}
|
||||||
.navDestination(this.pageMap)
|
.navDestination(this.pageMap)
|
||||||
.hideNavBar(true)
|
.hideNavBar(true)
|
||||||
.mode(NavigationMode.Stack)
|
.mode(NavigationMode.Stack)
|
||||||
|
|
||||||
|
// 根层启动页:从 app 启动持续到大厅首帧上屏,跨页面切换无缝(无白闪、无二次 splash)
|
||||||
|
if (this.splashVisible) {
|
||||||
|
Stack() {
|
||||||
|
Image($r('app.media.launch_image'))
|
||||||
|
.width('100%').height('100%')
|
||||||
|
.objectFit(ImageFit.Cover)
|
||||||
|
Column({ space: 14 }) {
|
||||||
|
if (this.splashBlocked === '') {
|
||||||
|
Progress({ value: this.splashPercent, total: 100, type: ProgressType.Linear })
|
||||||
|
.width('56%')
|
||||||
|
.color(Index.GOLD)
|
||||||
|
.backgroundColor('#E6E6E6')
|
||||||
|
Row({ space: 8 }) {
|
||||||
|
LoadingProgress().width(20).height(20).color(Index.GOLD)
|
||||||
|
Text(`${this.splashLabel} ${this.splashPercent}%`).fontSize(15).fontColor(Index.NAVY)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text(this.splashBlocked)
|
||||||
|
.fontSize(16).fontColor(Index.NAVY).textAlign(TextAlign.Center)
|
||||||
|
.backgroundColor('rgba(255,255,255,0.9)').borderRadius(8).padding(14)
|
||||||
|
.margin({ left: 24, right: 24 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.width('100%').height('100%')
|
||||||
|
.justifyContent(FlexAlign.End)
|
||||||
|
.padding({ bottom: 24 })
|
||||||
|
}
|
||||||
|
.width('100%').height('100%')
|
||||||
|
.backgroundColor(Color.White)
|
||||||
|
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.width('100%').height('100%')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,18 @@
|
|||||||
import { common } from '@kit.AbilityKit';
|
import { common } from '@kit.AbilityKit';
|
||||||
import { StartupOrchestrator, StartupResult, StartupStage } from 'domain_resource';
|
import { StartupOrchestrator, StartupResult, StartupStage } from 'domain_resource';
|
||||||
import { RouteName, BridgeGameParams } from '../routes/AppRoutes';
|
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)。
|
* 启动引导页(对应 weclomeactivity1,框架 §4/§8.1)——**纯启动逻辑载体,无可见 UI**。
|
||||||
* 进入即跑 StartupOrchestrator(本地配置→远程配置→资源准备→app_data 注入),
|
* 进入即跑 StartupOrchestrator(本地配置→远程配置→资源准备→app_data 注入),进度/文案/阻断写入
|
||||||
* 完成后 replace 进大厅容器;被 showmessage 阻断时弹公告。
|
* AppStorage,由 Index 根层启动页覆盖层统一呈现;完成后 replace 进大厅容器(覆盖层在大厅首帧后撤)。
|
||||||
*
|
*
|
||||||
* 启动时序:系统启动窗用透明 startWindowIcon($media:start_window_blank) + 白底 →
|
* 视觉放在 Index 根层而非本页:使 splash→大厅的页面切换发生在覆盖层之下,彻底消除导航空隙白闪。
|
||||||
* **纯白屏**;随后本页淡入全屏启动图 launch_image(白底 + 金色 logo)。系统窗无 logo,
|
|
||||||
* 故不存在"两套资源比例不一致"问题。覆盖层文字用品牌 navy(白底可见),进度条金色,
|
|
||||||
* 展示当前阶段中文文案。
|
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
export struct SplashPage {
|
export struct SplashPage {
|
||||||
pathStack: NavPathStack = new NavPathStack();
|
pathStack: NavPathStack = new NavPathStack();
|
||||||
@State private stage: StartupStage = StartupStage.INIT;
|
|
||||||
@State private percent: number = 0;
|
|
||||||
@State private blocked: string = '';
|
|
||||||
|
|
||||||
// 品牌色(取自 logo):navy 文字 + 金色进度
|
|
||||||
private static readonly NAVY: string = '#1B2A4A';
|
|
||||||
private static readonly GOLD: string = '#D2A312';
|
|
||||||
|
|
||||||
aboutToAppear(): void {
|
aboutToAppear(): void {
|
||||||
this.runStartup();
|
this.runStartup();
|
||||||
@@ -30,26 +21,27 @@ export struct SplashPage {
|
|||||||
private async runStartup(): Promise<void> {
|
private async runStartup(): Promise<void> {
|
||||||
const ctx: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
|
const ctx: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
|
||||||
const orchestrator = new StartupOrchestrator(ctx, (stage: StartupStage, percent: number) => {
|
const orchestrator = new StartupOrchestrator(ctx, (stage: StartupStage, percent: number) => {
|
||||||
this.stage = stage;
|
AppStorage.setOrCreate(KEY_SPLASH_PERCENT, percent);
|
||||||
this.percent = percent;
|
AppStorage.setOrCreate(KEY_SPLASH_LABEL, SplashPage.stageLabel(stage, percent));
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
const result: StartupResult = await orchestrator.run();
|
const result: StartupResult = await orchestrator.run();
|
||||||
if (result.enter) {
|
if (result.enter) {
|
||||||
|
// 切到大厅:保持 splashVisible=true,根层覆盖层持续盖住,由大厅首帧 onPageEnd 撤除。
|
||||||
const params: BridgeGameParams = { entryUrl: result.entryUrl, launchType: '0' };
|
const params: BridgeGameParams = { entryUrl: result.entryUrl, launchType: '0' };
|
||||||
this.pathStack.replacePathByName(RouteName.BRIDGE_GAME, params, false);
|
this.pathStack.replacePathByName(RouteName.BRIDGE_GAME, params, false);
|
||||||
} else {
|
} else {
|
||||||
this.blocked = result.message;
|
AppStorage.setOrCreate(KEY_SPLASH_BLOCKED, result.message);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const err = e as Error;
|
const err = e as Error;
|
||||||
this.blocked = `启动失败:${err.message}`;
|
AppStorage.setOrCreate(KEY_SPLASH_BLOCKED, `启动失败:${err.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 阶段 → 用户可读中文文案(PREPARE_RESOURCE 按进度区分"准备资源/下载更新")。 */
|
/** 阶段 → 用户可读中文文案(PREPARE_RESOURCE 按进度区分"准备资源/下载更新")。 */
|
||||||
private stageLabel(): string {
|
private static stageLabel(stage: StartupStage, percent: number): string {
|
||||||
switch (this.stage) {
|
switch (stage) {
|
||||||
case StartupStage.INIT:
|
case StartupStage.INIT:
|
||||||
return '正在启动…';
|
return '正在启动…';
|
||||||
case StartupStage.LOAD_LOCAL_CONFIG:
|
case StartupStage.LOAD_LOCAL_CONFIG:
|
||||||
@@ -62,7 +54,7 @@ export struct SplashPage {
|
|||||||
return '检测升级版本…';
|
return '检测升级版本…';
|
||||||
case StartupStage.PREPARE_RESOURCE:
|
case StartupStage.PREPARE_RESOURCE:
|
||||||
// 35% 为内置资源准备;55%~85% 为远程 zip 下载
|
// 35% 为内置资源准备;55%~85% 为远程 zip 下载
|
||||||
return this.percent >= 55 ? '正在下载更新…' : '准备游戏资源…';
|
return percent >= 55 ? '正在下载更新…' : '准备游戏资源…';
|
||||||
case StartupStage.INJECT_APP_DATA:
|
case StartupStage.INJECT_APP_DATA:
|
||||||
return '即将进入大厅…';
|
return '即将进入大厅…';
|
||||||
case StartupStage.ENTER_HALL:
|
case StartupStage.ENTER_HALL:
|
||||||
@@ -73,52 +65,10 @@ export struct SplashPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
|
// 视觉由 Index 根层启动页覆盖层呈现;本页仅承载启动逻辑(透明、无内容)
|
||||||
NavDestination() {
|
NavDestination() {
|
||||||
Stack() {
|
|
||||||
// 全屏启动图(白底 + 金色 logo)
|
|
||||||
Image($r('app.media.launch_image'))
|
|
||||||
.width('100%')
|
|
||||||
.height('100%')
|
|
||||||
.objectFit(ImageFit.Cover)
|
|
||||||
// 启动进度/公告叠加在底部(深色文字,白底可见)
|
|
||||||
Column({ space: 14 }) {
|
|
||||||
if (this.blocked === '') {
|
|
||||||
Progress({ value: this.percent, total: 100, type: ProgressType.Linear })
|
|
||||||
.width('56%')
|
|
||||||
.color(SplashPage.GOLD)
|
|
||||||
.backgroundColor('#E6E6E6')
|
|
||||||
// 阶段提示文字放在进度条下方
|
|
||||||
Row({ space: 8 }) {
|
|
||||||
LoadingProgress()
|
|
||||||
.width(20)
|
|
||||||
.height(20)
|
|
||||||
.color(SplashPage.GOLD)
|
|
||||||
Text(`${this.stageLabel()} ${this.percent}%`)
|
|
||||||
.fontSize(15)
|
|
||||||
.fontColor(SplashPage.NAVY)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Text(this.blocked)
|
|
||||||
.fontSize(16)
|
|
||||||
.fontColor(SplashPage.NAVY)
|
|
||||||
.textAlign(TextAlign.Center)
|
|
||||||
.backgroundColor('rgba(255,255,255,0.9)')
|
|
||||||
.borderRadius(8)
|
|
||||||
.padding(14)
|
|
||||||
.margin({ left: 24, right: 24 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.width('100%')
|
|
||||||
.height('100%')
|
|
||||||
.justifyContent(FlexAlign.End)
|
|
||||||
// 横屏(auto_rotation_landscape)下屏幕"高"为短边,过大的底边距会把进度块顶进居中 logo。
|
|
||||||
// 以底部为基准只留一小段间距,使进度/文字落在 logo 下方的空白带、贴近底部(系统手势条由安全区自动避让)。
|
|
||||||
.padding({ bottom: 24 })
|
|
||||||
}
|
|
||||||
.width('100%')
|
|
||||||
.height('100%')
|
|
||||||
.backgroundColor(Color.White)
|
|
||||||
}
|
}
|
||||||
.hideTitleBar(true)
|
.hideTitleBar(true)
|
||||||
|
.backgroundColor(Color.Transparent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user