Dialog类型NavDestination蒙层动画不流畅
问题现象
使用NavDestinationMode.DIALOG默认转场动画,出现如下2个问题:
期望退出时蒙层渐隐,同时内容区域向下退出。
解决措施
在onWillAppear、onWillDisappear生命周期执行背景色动画,示例如下:
@Builder
export function DialogNavDestinationBuilder() {
DialogNavDestination();
}
@Component
export struct DialogNavDestination {
stack: NavPathStack = AppStorage.get<NavPathStack>('basicNavigationStack')!;
@State backColor: ResourceColor = '#00000000';
build() {
NavDestination() {
Stack() {
Text('Dialog')
.fontSize(44)
.backgroundColor(Color.White)
}
.width('100%')
.height('100%')
}
.hideTitleBar(true)
.backgroundColor(this.backColor)
.mode(NavDestinationMode.DIALOG)
.onWillAppear(() => {
// 启动时蒙层渐现
this.getUIContext().animateTo({ duration:450 }, () => {
this.backColor = '#66000000';
});
})
.onWillDisappear(() => {
// 消失时蒙层渐隐
this.getUIContext().animateTo({ duration: 450 }, () => {
this.backColor = '#00000000';
});
})
}
} 
router、navigation动画冲突
问题现象
router跳到navigation页面,navigation在aboutToAppear回调里马上push一个NavDestination页面,这样会导致page和NavDestination页面同时显示动画,效果不好。
解决措施
关闭aboutToAppear中push的动画:
@Entry
@Component
struct NavigationPage {
navStack: NavPathStack = new NavPathStack();
aboutToAppear(): void {
AppStorage.setOrCreate<NavPathStack>('basicNavigationStack', this.navStack);
this.navStack.pushPath({ name: 'animation-BasicNavDestination' }, false); // 关闭本次push动画即可
}
build() {
Navigation(this.navStack) {
// ...
}
}
} pop、push同时进行却执行pop动画
问题现象
先pop栈顶页面,再马上push一个页面,动画效果是栈顶页面pop的动画,并不是PageOne的push动画。
this.stack.pop();
this.stack.pushPath({ name: 'animation-BasicNavDestination' }); 解决措施
首先在一次操作中,不管调用了多少次pop、push接口,Navigation会计算最终结果,并且只做一次动画。
当前Navigation页面切换的动画会以操作前的栈顶页面为主,如果栈顶页面在操作后不在栈中,则执行pop动画,若在栈中则执行push动画。
例如,假设当前栈顶是PageTop,先执行pop将PageTop移除再执行push将PageNew入栈,本次操作完成后PageTop页面已不在栈中,所以最终执行的动画是pop动画。
如果想移除页面的同时push另一个页面并且执行push动画,可以将push的页面设置为NEW_INSTANCE,默认执行push动画:
this.stack.pop();
this.stack.pushPath({ name: 'animation-BasicNavDestination' }, { launchMode: LaunchMode.NEW_INSTANCE }); 如何实现Navigation和NavDestination之间的共享元素转场
目前仅NavDestination间的跳转支持共享元素转场动效,NavBar与NavDestination间的跳转系统暂不支持共享元素动效。
NavDestination的共享元素转场需要配合geometryTransition接口实现,并且:
示例请参考:共享元素转场。
NavDestination的builder中使用多个if语句时页面空白
问题现象
在Navigation的navDestination属性指定的builder中使用多个独立的if语句分别构建NavDestination页面,跳转到部分页面时页面空白。例如以下用法,当name为PageC时页面空白:
@Entry
@Component
struct ExamplePage {
private stack: NavPathStack = new NavPathStack();
@Builder
pageMap(name: string) {
if (name === 'PageA') {
NavDestination()
.backgroundColor(Color.Red)
.title('PageA')
}
if (name === 'PageB') {
NavDestination()
.backgroundColor(Color.Green)
.title('PageB')
}
if (name === 'PageC') {
NavDestination()
.backgroundColor(Color.Blue)
.title('PageC')
}
}
build() {
Navigation(this.stack) {
Button("push PageA").onClick((event: ClickEvent) => {
this.stack.pushPathByName("PageA", null)
})
Button("push PageB").onClick((event: ClickEvent) => {
this.stack.pushPathByName("PageB", null)
})
Button("push PageC").onClick((event: ClickEvent) => {
this.stack.pushPathByName("PageC", null)
})
}
.navDestination(this.pageMap)
}
} 原因分析
Navigation构建NavDestination的机制:
- builder执行后,第一个节点作为根节点,其余节点按照代码顺序依次挂载到该根节点下(与第一个节点并列的节点会当成子节点挂载)。
- Navigation查找NavDestination节点时,从根节点沿第一个子节点逐层向下遍历,直到找到NavDestination为止。如果某一层的第一个子节点无子节点,遍历就会终止,无法继续查找后续兄弟节点。
每个独立的if语句会产生一个IfElseNode,第二、三个IfElseNode作为第一个IfElseNode的子节点并列挂载,NavDestination则挂载在对应条件为true的IfElseNode下。
-
当name === 'PageA'时,IfElseNode1为true,IfElseNode2和IfElseNode3为false,产生的组件树:
IfElseNode1 ← 根节点
├── NavDestination(PageA) ← IfElseNode1为true,NavDestination作为IfElseNode1的第一个子节点
├── IfElseNode2 ← IfElseNode2为false,无子节点
└── IfElseNode3 ← IfElseNode3为false,无子节点
Navigation组件从IfElseNode1沿第一个子节点遍历,找到NavDestination(PageA),页面正常。
-
当name === 'PageB'时,IfElseNode2为true,IfElseNode1和IfElseNode3为false,产生的组件树:
IfElseNode1 ← 根节点
├── IfElseNode2 ← IfElseNode2为true
│ └── NavDestination(PageB)
└── IfElseNode3 ← IfElseNode3为false,无子节点
Navigation组件从IfElseNode1沿第一个子节点遍历:IfElseNode1 → IfElseNode2 → NavDestination(PageB),页面正常。
-
当name === 'PageC'时,IfElseNode3为true,IfElseNode1和IfElseNode2为false,产生的组件树:
IfElseNode1 ← 根节点
├── IfElseNode2 ← IfElseNode2为false,无子节点(第一个子节点!)
└── IfElseNode3 ← IfElseNode3为true
└── NavDestination(PageC) Navigation组件IfElseNode1沿第一个子节点遍历:IfElseNode1 → IfElseNode2(无子节点,遍历终止),无法找到NavDestination(PageC),页面空白。
解决措施
将builder中的多个独立if语句改为if-else if-else判断,只产生一个IfElseNode作为根节点,确保Navigation能够遍历找到NavDestination:
@Entry
@Component
struct NavDestinationSystemTransition {
// ...
@Builder
pageMap(name: string) {
if (name === 'Fade') {
Fade();
} else if (name === 'Explode') {
Explode();
} else if (name === 'SlideRight') {
SlideRight();
} else if (name === 'SlideBottom') {
SlideBottom();
} else {
Dest();
}
}
aboutToAppear(): void {
this.stack.pushPath({name: 'Dest'});
}
build() {
Navigation(this.stack) {
// ...
}
.navDestination(this.pageMap)
.hideNavBar(true)
}
}