HarmonyOS《柚兔学伴》项目实战06-闪屏页与动画
06 闪屏页与动画
闪屏页(Splash Screen)是应用启动后的第一个页面,承担着品牌展示、资源预加载和页面跳转的职责。本文将围绕「柚兔学伴」项目的 SplashPage.ets 和 SplashViewModel.ets,讲解如何利用 ArkUI 的页面生命周期、animateTo 全局动画 API 以及事件驱动的 ViewModel 架构来构建一个完整的闪屏页。
1. SplashPage 整体结构
SplashPage 使用 @Entry + @Component 装饰器声明为页面入口组件:
import { ConfigurationConstant } from '@kit.AbilityKit';
import { CommonConstants, Logger, PageContext, WindowUtil } from 'common';
import { SplashEventTypeEnum, SplashViewModel } from '../viewmodel/SplashViewModel';
@Entry
@Component
struct SplashPage {
private pageContext: PageContext = AppStorage.get('pageContext') as PageContext;
private appPathInfo: NavPathStack = this.pageContext.navPathStack;
private viewModel: SplashViewModel = new SplashViewModel();
// ...
}
关键点:
@Entry:将该组件标记为页面路由入口,框架会自动注册路由表。PageContext:从AppStorage获取全局页面上下文,其中包含NavPathStack导航栈,用于页面跳转。SplashViewModel:事件驱动的 ViewModel 实例,负责处理业务逻辑。
2. 页面生命周期与动画流程
闪屏页的核心逻辑集中在 aboutToAppear 生命周期回调中:
aboutToAppear(): void {
WindowUtil.updateStatusBarColor(getContext(this), true);
this.viewModel.sendEvent(SplashEventTypeEnum.CHECK_FIRST_START);
animateTo({
delay: CommonConstants.ANIMATION_DELAY,
duration: CommonConstants.ANIMATION_DURATION,
onFinish: () => {
this.viewModel.sendEvent(SplashEventTypeEnum.JUMP_TO_MAIN);
}
}, () => {
this.viewModel.sendEvent(SplashEventTypeEnum.PRELOAD_RESOURCES);
})
}
2.1 执行时序
页面加载后,按照以下时序依次执行:
- 更新状态栏颜色——
WindowUtil.updateStatusBarColor传入true表示使用浅色状态栏(白底黑字),以匹配闪屏背景。 - 检查首次启动——发送
CHECK_FIRST_START事件,ViewModel 判断是否首次打开应用并记录标记。 - 启动动画——调用
animateTo,在动画闭包中发送PRELOAD_RESOURCES事件预加载资源。 - 动画结束跳转——
onFinish回调中发送JUMP_TO_MAIN事件,导航到主页。
2.2 animateTo 全局动画 API
animateTo 是 ArkUI 提供的全局隐式动画接口,签名如下:
animateTo(value: AnimateParam, task: () => void): void
| 参数 | 说明 |
|---|---|
value |
动画参数对象,包含 duration、delay、curve、onFinish 等 |
task |
动画闭包,闭包内触发的属性变化会自动以动画形式过渡 |
在本项目中:
delay: CommonConstants.ANIMATION_DELAY(500ms)——延迟启动,确保页面渲染完毕。duration: CommonConstants.ANIMATION_DURATION(1000ms)——动画持续时长。onFinish——动画完成后的回调,用于触发页面跳转。
注意:
animateTo内的闭包应当只包含 UI 属性修改。如果只需延时执行逻辑而不涉及 UI 属性动画,也可以考虑使用setTimeout替代。本项目中闭包内调用PRELOAD_RESOURCES事件,是在动画期间并行执行预加载任务。
3. 事件驱动的 ViewModel
SplashViewModel 继承自 BaseVM<BaseState>,采用事件驱动架构:
3.1 BaseVM 抽象基类
// common/src/main/ets/viewmodel/BaseVM.ets
export abstract class BaseVM<T extends BaseState> {
protected state: T;
public constructor(initialState: T) {
this.state = initialState;
}
getState(): T {
return this.state;
}
public abstract sendEvent(baseVMEvent: BaseVMEvent);
}
设计要点:
- 泛型
T extends BaseState:每个 ViewModel 可以定义自己的状态类型。 sendEvent抽象方法:子类必须实现,通过事件类型分发到不同的处理方法。getState:暴露当前状态给 UI 层读取。
3.2 SplashViewModel 实现
export class SplashViewModel extends BaseVM<BaseState> {
private pageContext: PageContext = AppStorage.get('pageContext') as PageContext;
private preferenceManager: PreferenceManager = PreferenceManager.getInstance();
public constructor() {
super(new BaseState());
}
public sendEvent(eventType: SplashEventTypeEnum): void {
if (eventType === SplashEventTypeEnum.JUMP_TO_MAIN) {
this.jumpToMainPage();
} else if (eventType === SplashEventTypeEnum.PRELOAD_RESOURCES) {
this.preloadResources();
} else if (eventType === SplashEventTypeEnum.CHECK_FIRST_START) {
this.checkIsFirstStart();
}
}
}
3.3 事件枚举
export enum SplashEventTypeEnum {
JUMP_TO_MAIN = 'jumpToMainPage',
PRELOAD_RESOURCES = 'preloadResources',
CHECK_FIRST_START = 'checkIsFirstStart',
}
使用枚举而非硬编码字符串,确保事件名的类型安全和可维护性。
3.4 具体事件处理
跳转到主页:
private jumpToMainPage(): void {
this.pageContext.replacePage({
routerName: 'MainPage',
});
}
使用 replacePage 而非 pushPage,意味着闪屏页会从导航栈中移除,用户无法返回闪屏页。
检查首次启动:
private checkIsFirstStart(): void {
this.preferenceManager.hasValue('isFirstStart').then((hasResult: boolean) => {
if (hasResult) {
Logger.info(TAG, 'Not first startup.');
} else {
Logger.info(TAG, 'First startup.');
this.preferenceManager.setValue('isFirstStart', false).then(() => {
Logger.info(TAG, 'Put the value of startup Successfully.');
}).catch((err: BusinessError) => {
Logger.error(TAG, `Put the value of startup Failed, err code: ${err.code}`);
});
}
});
}
通过 PreferenceManager 持久化存储首次启动标记,基于 @ohos.data.preferences 实现。
4. UI 构建:Navigation 与背景图
build() {
Navigation(this.appPathInfo) {
Column()
.width('100%')
.height('100%')
.backgroundImage($r('app.media.ic_splash'))
.backgroundImageSize({ width: '100%', height: '100%' })
}
.hideTitleBar(true)
.mode(NavigationMode.Stack)
.height('100%')
.width('100%')
}
4.1 Navigation 容器
Navigation(this.appPathInfo):绑定NavPathStack导航栈,支持页面路由。.hideTitleBar(true):隐藏标题栏,闪屏页需要全屏展示。.mode(NavigationMode.Stack):使用栈管理模式,支持pushPage/replacePage等路由操作。
4.2 背景图
$r('app.media.ic_splash'):引用resources/base/media/ic_splash资源,这是 ArkUI 的资源引用语法$r('app.type.name')。backgroundImageSize:设为{ width: '100%', height: '100%' }确保图片铺满整个容器。
5. onPageHide:状态栏颜色恢复
当用户离开闪屏页(跳转到主页后),需要将状态栏颜色恢复为与系统深浅色模式匹配:
onPageHide() {
Logger.info(TAG, 'onPageHide');
WindowUtil.updateStatusBarColor(getContext(this),
AppStorage.get('systemColorMode') === ConfigurationConstant.ColorMode.COLOR_MODE_DARK);
}
onPageHide:页面隐藏时的生命周期回调。AppStorage.get('systemColorMode'):从全局存储读取当前颜色模式。- 当系统为深色模式时,传入
true(浅色状态栏);否则传入false(深色状态栏)。
6. 总结
| 知识点 | 说明 |
|---|---|
@Entry + @Component |
页面入口组件声明 |
aboutToAppear |
页面出现前回调,适合初始化逻辑 |
animateTo |
全局隐式动画 API,支持 delay/duration/onFinish |
BaseVM<T> |
抽象 ViewModel 基类,事件驱动架构 |
sendEvent + 枚举 |
类型安全的事件分发机制 |
Navigation + NavPathStack |
声明式导航与路由栈管理 |
$r() |
资源引用语法,访问 media/string/color 资源 |
onPageHide |
页面隐藏回调,用于资源清理或状态恢复 |
PreferenceManager |
轻量级键值对持久化存储 |
闪屏页看似简单,却串联了 ArkUI 的页面生命周期、动画系统、导航框架和 ViewModel 架构等多个核心概念。掌握这些模式,将为后续复杂页面的开发打下坚实基础。
更多推荐


所有评论(0)