HarmonyOS鸿蒙Next中ArkUI路由/导航系列七:Navigation自定义转场动画,让页面切换炫起来
HarmonyOS鸿蒙Next中ArkUI路由/导航系列七:Navigation自定义转场动画,让页面切换炫起来
1、Navigation自定义动画
1.1 准备工作
开发者需要声明自定义动画的管理类,同时创建全局单实例的CustomTransition,以便于管理自定义动画的参数注册和获取,如下是一个简单的示例,开发者也可根据自己的业务需求去实现:
// CustomNavigationUtils.ts 工具类,用来管理所有页面的自定义动画参数注册和获取等
// 自定义接口,用来保存某个页面相关的转场动画回调和参数
export interface AnimateCallback {
start: ((isPush: boolean, isExit: boolean) => void | undefined) | undefined;
finish: ((isPush: boolean, isExit: boolean) => void | undefined) | undefined;
}
const customTransitionMap: Map<string, AnimateCallback> = new Map();
export class CustomTransition {
static delegate = new CustomTransition();
static getInstance() {
return CustomTransition.delegate;
}
/* 注册某个页面的动画回调
* name: 注册页面的唯一id
* startCallback:用来设置动画开始时页面的状态
* endCallback:用来设置动画结束时页面的状态
*/
registerNavParam(name: string, startCallback: (isPush: boolean, isExit: boolean) => void,
endCallback: (isPush: boolean, isExit: boolean) => void): void {
if (customTransitionMap.has(name)) {
let param = customTransitionMap.get(name);
if (param != undefined) {
param.start = startCallback;
param.finish = endCallback;
return;
}
}
let params: AnimateCallback = { start: startCallback, finish: endCallback };
customTransitionMap.set(name, params);
}
unRegisterNavParam(name: string): void {
customTransitionMap.delete(name);
}
getAnimateParam(name: string): AnimateCallback {
let result: AnimateCallback = {
start: customTransitionMap.get(name)?.start,
finish: customTransitionMap.get(name)?.finish
};
return result;
}
}
1.2 代码实现
对于Navigation组件,需要设置属性customNavContentTransition,该属性需要传入NavigationAnimatedTransition类型的参数delegate。在路由操作发生时,系统侧会回调至delegate,并将页面切换的from、to以及操作类型信息返回给该函数作为入参。开发者可以在delete中根据系统回传的信息,来决定执行什么类型的自定义动画,如下示例:
// NavigationCustomAniMain.ets
@Component
struct NavigationCustomTransitionExample {
pageInfos: NavPathStack = new NavPathStack();
aboutToAppear() {
this.pageInfos.pushPath({ name: 'navigationAniPageOne' }, false);
}
build() {
Navigation(this.pageInfos) {
}
.hideNavBar(true)
// 实现属性customNavContentTransition,以定义动画的规则
.customNavContentTransition((from: NavContentInfo, to: NavContentInfo, operation: NavigationOperation) => {
// 首页不进行自定义动画
if (from.index === -1 || to.index === -1) {
return undefined;
}
let customAnimation: NavigationAnimatedTransition = {
timeout: 2000,
// 转场开始时系统调用该方法,并传入转场上下文代理对象
transition: (transitionProxy: NavigationTransitionProxy) => {
if (!from.navDestinationId || !to.navDestinationId) {
return;
}
// 从封装类CustomTransition中根据子页面的序列获取对应的转场动画回调
let fromParam: AnimateCallback = CustomTransition.getInstance().getAnimateParam(from.navDestinationId);
let toParam: AnimateCallback = CustomTransition.getInstance().getAnimateParam(to.navDestinationId);
// Push动画
if (operation == NavigationOperation.PUSH) {
if (fromParam.start && toParam.start) {
// 设置Push转场的两个页面的动画起点
fromParam.start(true, true);
toParam.start(true, false);
}
this.getUIContext()?.animateTo({
duration: 500, curve: Curve.Friction, onFinish: () => {
// 动画结束后需要手动调用finishTransition,否则在timeout时间后由系统调用
transitionProxy.finishTransition();
}
}, () => {
if (fromParam.finish && toParam.finish) {
// 设置Push转场的两个页面的动画终点
fromParam.finish(true, true);
toParam.finish(true, false);
}
})
} else if (operation == NavigationOperation.POP) {
// Pop动画
if (fromParam.start && toParam.start) {
// 设置Pop转场的两个页面的动画起点
fromParam.start(false, true);
toParam.start(false, false);
}
this.getUIContext()?.animateTo({
duration: 500, curve: Curve.Friction, onFinish: () => {
// 动画结束后需要手动调用finishTransition,否则在timeout时间后由系统调用
transitionProxy.finishTransition();
}
}, () => {
if (fromParam.finish && toParam.finish) {
// 设置Pop转场的两个页面的动画终点
fromParam.finish(false, true);
toParam.finish(false, false);
}
})
} else {
// Replace不做动画
}
}
};
return customAnimation;
})
}
}
同时,开发者需要为每个NavDestination注册页面自身的动画参数,以供Navigation.customNavContentTransition回调内部的逻辑调用,例如:
// NavigationCustomAniMain.ets
@Component
export struct PageContainer {
pageInfos: NavPathStack = new NavPathStack();
@State translateY: string = '0';
pageId: string = '';
title: string = ''
registerCallback() {
CustomTransition.getInstance().registerNavParam(this.pageId,
// 设置转场动画起点,根据不同的转场类型分别设置
(isPush: boolean, isExit: boolean) => {
if (isPush) {
if (isExit) {
this.translateY = '0';
} else {
this.translateY = '100%';
}
} else {
if (isExit) {
this.translateY = '0';
} else {
this.translateY = '0';
}
}
},
// 设置转场动画终点,根据不同的转场类型分别设置
(isPush: boolean, isExit: boolean) => {
if (isPush) {
if (isExit) {
this.translateY = '0';
} else {
this.translateY = '0';
}
} else {
if (isExit) {
this.translateY = '100%';
} else {
this.translateY = '0';
}
}
});
}
build() {
NavDestination() {
Column() {
Button('push next page')
.width(330)
.margin(7)
.onClick(() => {
this.pageInfos.pushPath({ name: this.title == 'PageOne' ? "navigationAniPageTwo" : "navigationAniPageOne" });
})
}
.size({ width: '100%', height: '100%' })
}
.title(this.title)
.onDisAppear(() => {
// 页面销毁时解注册自定义转场动画参数
CustomTransition.getInstance().unRegisterNavParam(this.pageId);
})
.onReady((context: NavDestinationContext) => {
this.pageInfos = context.pathStack;
if (context.navDestinationId) {
this.pageId = context.navDestinationId;
// 页面创建时注册自定义转场动画参数
this.registerCallback();
}
})
.translate({ y: this.translateY })
.backgroundColor(this.title == 'PageOne' ? '# F1F3F5' : '# ff11dee5')
}
}
2、NavDestination自定义动画
开发者如果仅想控制单个NavDestination的转场动画,而无需关心其他NavDestination的动画时,可以通过在NavDestination中设置customTransition属性来为当前页面配置自定义动画。该属性接收类型为NavDestinationTransitionDelegate的入参作为动画代理。与Navigation动画类似,动画代理会在页面切换时执行相,并发起相应的自定义动画,例如如下设置的NavDestination组件:
// CustomNavDest.ets
@Component
struct CustomAniNavDestination {
name: string = 'NA'
stack: NavPathStack = new NavPathStack()
@State translateY: string = '0';
build() {
NavDestination() {
Button('push next page')
.width(330)
.margin(7)
.onClick(() => {
this.stack.pushPath({ name: this.name == 'PageOne' ? "navDestCustomAniPageTwo" : "navDestCustomAniPageOne" });
})
}
.translate({ y: this.translateY })
.backgroundColor(this.name == 'PageOne' ? '# F1F3F5' : '# ff11dee5')
.onReady((context) => {
this.stack = context.pathStack
})
.title(this.name)
.customTransition(
(op: NavigationOperation, isEnter: boolean)
: Array<NavDestinationTransition> | undefined => {
console.log('[NavDestinationTransition]', 'reached delegate in frontend, op: ' + op + ', isEnter: ' + isEnter);
// navDestination动画的事件。navDestination自定义动画会基于当前组件的UI状态与事件执行后的UI状态来发起动画。
let transitionOneEvent = () => { console.log('[NavDestinationTransition]', 'reached transitionOne, empty now!') }
if (op === NavigationOperation.PUSH) {
if (isEnter) {
// ENTER_PUSH
// 为入场的PUSH页面设置动画
console.log('[NavDestinationTransition]', 'push & isEnter, init y to 100%');
this.translateY = '100%';
transitionOneEvent = () => {
console.log('[NavDestinationTransition]', 'push & isEnter, finally set y to 0');
this.translateY = '0';
}
}
} else if (op === NavigationOperation.POP) {
if (!isEnter) {
// EXIT_POP
// 为离场的POP页面设置动画
console.log('[NavDestinationTransition]', 'pop & isExit, init y to 0');
this.translateY = '0';
transitionOneEvent = () => {
console.log('[NavDestinationTransition]', 'pop & isExit, finally set y to 100%');
this.translateY = '100%';
}
}
} else if (op === NavigationOperation.REPLACE) {
console.log('[NavDestinationTransition]', 'REPLACE not support');
} else {
console.log('[NavDestinationTransition]', 'invalid operation!');
}
// 根据以上逻辑,确定动画的行为,并创建类型为NavDestinationTransition的动画实例transitionOne
let transitionOne: NavDestinationTransition = {
duration: 500,
delay: 0,
curve: Curve.Friction,
event: transitionOneEvent,
onTransitionEnd: () => { console.log('[NavDestinationTransition]', 'reached transitionOneFinish, empty now!') }
};
// 将transitionOne作为返回值传递给系统,系统侧将发起transitionOne对应的动画
return [ transitionOne ];
})
}
}
3、动画效果演示
上面章节展示的自定义动画均如下图所示
(动图待补)
4、示例源码
(链接待补)
(未完待续……)
更多关于HarmonyOS鸿蒙Next中ArkUI路由/导航系列七:Navigation自定义转场动画,让页面切换炫起来的实战教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next中,ArkUI的Navigation组件支持自定义转场动画。通过PageTransitionEnter
和PageTransitionExit
接口,开发者可以定义页面进入和退出时的动画效果。使用动画属性(如translate、opacity、scale)配合curve曲线函数,可实现平滑过渡效果。需在pageTransition函数中配置动画参数,系统会自动应用于Navigation的页面切换。支持共享元素转场,通过matchedContent属性关联不同页面的组件。动画时长建议控制在300-500毫秒。
更多关于HarmonyOS鸿蒙Next中ArkUI路由/导航系列七:Navigation自定义转场动画,让页面切换炫起来的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
HarmonyOS Next Navigation自定义转场动画解析
从技术实现来看,HarmonyOS Next提供了两种级别的自定义转场动画控制方式:
1. Navigation级别全局动画
通过customNavContentTransition
属性实现,主要特点:
- 适用于整个Navigation容器内所有页面切换
- 通过
NavigationAnimatedTransition
接口定义动画规则 - 需要配合
NavigationTransitionProxy
控制动画生命周期 - 示例中实现了上下滑动效果(PUSH时新页面从底部进入,POP时旧页面滑到底部)
关键点:
- 使用
CustomTransition
工具类管理各页面动画状态 - 在
transition
回调中区分PUSH/POP操作 - 必须调用
finishTransition()
结束动画
2. NavDestination级别单页动画
通过customTransition
属性实现,主要特点:
- 仅控制当前页面的转场效果
- 通过
NavDestinationTransitionDelegate
定义动画 - 更简单直接,无需全局管理
关键点:
- 根据
NavigationOperation
和isEnter
判断动画方向 - 通过修改组件状态(如translateY)实现动画效果
- 返回
NavDestinationTransition
数组定义具体动画参数
两种方式各有适用场景:全局动画适合统一风格,单页动画适合特殊需求。开发者可根据实际需求选择实现方式。