HarmonyOS 6.0 页面转场动画与共享元素进阶

发布时间:2026/7/27 14:05:30
HarmonyOS 6.0 页面转场动画与共享元素进阶 页面跳转时的动画不只是好看——它告诉用户你从哪来要到哪去。没有动画的跳转像电视换台突然蹦出一个新画面用户要花半秒才能反应过来。好的转场动画是空间连续的用户能感知到前后页面的逻辑关系。Navigation内置转场Navigation默认有推入/推出动画——新页面从右侧滑入返回时向右滑出。这是iOS/Android/HarmonyOS通用的标准转场。但不是所有场景都适合右滑。底部弹出的半模态、从中心展开的卡片、淡入淡出的tab切换都需要自定义转场。自定义NavDestination转场NavDestination支持自定义转场动画NavDestination(){// 页面内容}.transition(TransitionEffect.OPACITY)TransitionEffect提供几种预设预设效果OPACITY淡入淡出SLIDE(SlideEffect.Right)从右滑入SLIDE(SlideEffect.Left)从左滑入SLIDE(SlideEffect.Up)从下方滑入SLIDE(SlideEffect.Down)从上方滑入NONE无动画组合动画NavDestination(){// 页面内容}.transition(TransitionEffect.SLIDE(SlideEffect.Right).combine(TransitionEffect.OPACITY))combine把两个动画组合——同时滑动淡入。animateTo自定义转场更灵活的控制用animateTo。在pushPath后手动触发动画StatepageScale:number0.8StatepageOpacity:number0pushToDetail(){this.navPathStack.pushPath({name:Detail});animateTo({duration:300,curve:Curve.EaseOut},(){this.pageScale1;this.pageOpacity1;});}配合NavDestination的属性绑定NavDestination(){// 内容}.scale({x:this.pageScale,y:this.pageScale}).opacity(this.pageOpacity)这种方式需要自己管理动画状态但可以实现任意效果——缩放、旋转、3D翻转都行。共享元素转场共享元素转场Shared Transition让同一个元素在两个页面间平滑过渡。比如列表页的封面图飞到详情页的大图位置。第一步给两个页面的对应元素设相同的sharedTransition名称// 列表页Image(item.cover).width(120).height(80).sharedTransition(cover_item.id,{duration:300,curve:Curve.EaseInOut})// 详情页Image(this.item.cover).width(100%).height(240).sharedTransition(cover_this.item.id,{duration:300,curve:Curve.EaseInOut})名称必须完全一致。duration和curve也要匹配否则进出动画不协调。sharedTransition的名称必须唯一——如果列表中有多个图片用同一个名称动画会错乱。所以用’cover_’ id组合唯一名称。共享元素动画参数.sharedTransition(unique_name,{duration:300,curve:Curve.EaseInOut,delay:0,zIndex:0,type:SharedTransitionEffectType.Static})type有两种值Static静态共享元素直接从起点飞到终点中间不跟随手势Animated动画共享支持更流畅的过渡zIndex控制共享元素的层级——值越高越在上面。如果共享元素被其他元素遮挡加大zIndex。多元素共享多个元素同时做共享转场// 列表页Image(item.cover).sharedTransition(cover_item.id,{duration:300})Text(item.title).sharedTransition(title_item.id,{duration:300,delay:50})Text(item.author).sharedTransition(author_item.id,{duration:300,delay:100})// 详情页Image(this.item.cover).sharedTransition(cover_this.item.id,{duration:300})Text(this.item.title).sharedTransition(title_this.item.id,{duration:300,delay:50})Text(this.item.author).sharedTransition(author_this.item.id,{duration:300,delay:100})delay让各元素依次出发形成波浪效果。50ms间隔不会太密集也不会太分散。注意共享元素数量别超过5个。每个共享元素都需要独立的动画通道太多会掉帧。共享元素与页面背景共享元素动画期间两个页面都在渲染。如果页面背景不协调会出现穿帮——比如列表页白底、详情页灰底过渡期间背景切换很明显。解决方式让两个页面的背景色一致或者给共享元素加一个背景遮罩Image(item.cover).sharedTransition(cover_item.id,{duration:300,zIndex:10}).backgroundColor(Color.White).padding(8)给共享元素加白色背景padding让它包裹内容。动画时白色背景一起移动遮挡后面的背景切换。手势返回与共享元素手势返回时共享元素需要反向动画——从详情页飞回列表页。Navigation默认支持但有个前提列表页的共享元素在返回时还必须在屏幕上。如果列表是LazyForEach滚动到详情页后列表项可能已经被回收了。返回时找不到对应的共享元素动画就没了。解决方式在pushPath前记录被点击项的索引返回时先scrollToIndex让该项可见privateclickedItemIndex:number-1;// 跳转前记录onClick((){this.clickedItemIndexindex;this.navPathStack.pushPath({name:Detail,param:{id:item.id}});})// 返回后恢复aboutToAppear():void{this.navPathStack.on(pop,(){if(this.clickedItemIndex0){this.scroller.scrollToIndex(this.clickedItemIndex);}});}页面进入/退出动画除了共享元素页面本身也有进入和退出动画NavDestination(){Column(){// 页面内容}.width(100%).height(100%)}.onShown((){animateTo({duration:300},(){this.contentOpacity1;this.contentTranslateY0;});}).onHidden((){animateTo({duration:200},(){this.contentOpacity0;this.contentTranslateY20;});})onShown在页面显示时触发onHidden在页面被覆盖时触发。用animateTo驱动内容区的出现/消失动画。关键onShown/onHidden不等同于页面的mount/unmount。页面被覆盖新页面push到上面时触发onHidden但不销毁返回时触发onShown但不重新mount。所以动画状态要在onShown中重置。模态转场全屏模态弹出需要从底部滑入的转场StateshowSheet:booleanfalseStatesheetOffset:number100Column(){// 内容}.width(100%).height(100%).translate({y:this.showSheet?0:100}).animation({duration:300,curve:Curve.EaseOut})但这不是NavDestination是同一个页面内的视图切换。真正的模态用bindSheet或bindContentCover它们自带转场动画。转场动画性能转场动画期间两个页面同时渲染性能压力是平时的两倍。几个优化点减少共享元素数量不超过3个duration别太长300ms是舒适区超过500ms用户会觉得慢共享元素避免shadow/blur阴影和模糊的离屏渲染开销大关闭不必要的animation转场期间暂停其他动画用renderGroup把页面内容标记为渲染组减少重绘范围Column(){// 页面内容}.renderGroup(true)renderGroup让组件树整体绘制到离屏缓冲区转场动画只需要移动/缩放缓冲区不需要重绘每帧内容。踩坑清单问题原因解决共享元素没动画两个页面的名称不一致名称必须完全相同动画闪烁两个页面背景色不一致统一背景色或给元素加背景返回时动画丢失列表项被LazyForEach回收返回前scrollToIndex多个元素动画错乱用了相同的sharedTransition名称每个元素用唯一名称转场期间掉帧共享元素太多或有shadow不超过3个去掉阴影onShown中动画不执行页面已显示但状态没重置先重置状态再animateTocombine动画不生效transition写法有误用combine链式调用模态弹出没有动画直接改State显示/隐藏用bindSheet或加translate动画共享元素被遮挡zIndex太低加大sharedTransition的zIndex手势返回动画卡顿两个页面同时渲染太重加renderGroup(true)转场动画的原则共享元素不超过3个duration 300mscurve用EaseInOutzIndex保证在最上层。好的转场是克制的——只让最关键的视觉元素做动画其他元素安静地淡入淡出。