HarmonyOS7 展开折叠动画:asymmetric transition 的妙用

发布时间:2026/7/24 22:33:35
HarmonyOS7 展开折叠动画:asymmetric transition 的妙用 文章目录前言应用场景方案设计编码实现状态管理Builder 封装折叠项asymmetric 过渡详解状态管理辅助方法全部展开/折叠踩坑总结坑1transition 不加 animation 参数不生效坑2Builder 里的状态响应坑3折叠时内容闪烁写在最后前言FAQ 页面、设置页面、手风琴菜单……这些地方都需要展开/折叠的交互。点一下标题详细内容从下方展开再点一下内容收起消失。ArkUI 实现这个效果有两种路子一种是手动控制高度动画另一种是用transition配合if-else条件渲染。第二种更简洁今天就来聊聊特别是asymmetric不对称过渡在这里的应用。应用场景展开折叠在 App 里的应用场景比你想的多FAQ 问答页面设置选项的详细说明手风琴菜单评论区的展开更多表单的高级选项核心需求都一样点击触发展开再点击触发折叠展开和折叠都要有过渡动画。方案设计每个折叠项需要一个标题栏始终可见可点击一个内容区域展开时显示折叠时隐藏展开时的进入动画折叠时的退出动画关键难点在于展开和折叠的动画效果应该不同。展开时内容从上方向下滑出折叠时内容向上方收回。这就需要用到TransitionEffect.asymmetric。编码实现状态管理三个折叠项需要三个独立的状态变量EntryComponentstruct ExpandCollapsePanel{StateisExpanded1:booleanfalseStateisExpanded2:booleanfalseStateisExpanded3:booleanfalsebuild(){Column(){// ...}}}Builder 封装折叠项用Builder把折叠项的 UI 抽出来避免重复代码BuilderExpandItem(title:string,index:number){Column(){// 标题栏Row(){Text(title).fontSize(14).fontWeight(FontWeight.Medium).layoutWeight(1)Text(this._getState(index)?收起 ▲:展开 ▼).fontSize(12).fontColor(#4D96FF)}.width(100%).padding(12).onClick((){this._toggleState(index)})// 内容区域条件渲染if(this._getState(index)){Column(){Text(这是${title}的详细内容区域。\n展开折叠动画可以让页面更具交互性\n用户体验更加流畅。).fontSize(12).fontColor(#999999).padding(12)}.width(100%).backgroundColor(#F5F6FA).borderRadius(8).animation({duration:300,curve:Curve.EaseInOut}).transition(TransitionEffect.asymmetric(TransitionEffect.OPACITY.animation({duration:300}).combine(TransitionEffect.translate({y:-20}).animation({duration:300})),TransitionEffect.OPACITY.animation({duration:200}).combine(TransitionEffect.translate({y:20}).animation({duration:200}))))}}.width(100%).backgroundColor(#FFFFFF).borderRadius(8).margin({bottom:8})}asymmetric 过渡详解这段transition代码是整个案例的核心.transition(TransitionEffect.asymmetric(// 第一个参数出现时的效果TransitionEffect.OPACITY.animation({duration:300}).combine(TransitionEffect.translate({y:-20}).animation({duration:300})),// 第二个参数消失时的效果TransitionEffect.OPACITY.animation({duration:200}).combine(TransitionEffect.translate({y:20}).animation({duration:200}))))出现时展开透明度从 0→1 从上方 20px 处向下滑入持续 300ms消失时折叠透明度从 1→0 向下方 20px 处滑出持续 200ms注意消失动画比出现动画短200ms vs 300ms这是有意为之。用户点击折叠时希望内容赶紧收起来所以消失要快一些。状态管理辅助方法因为用了Builder需要通过辅助方法来管理状态_getState(index:number):boolean{if(index0)returnthis.isExpanded1if(index1)returnthis.isExpanded2returnthis.isExpanded3}_toggleState(index:number){if(index0)this.isExpanded1!this.isExpanded1elseif(index1)this.isExpanded2!this.isExpanded2elsethis.isExpanded3!this.isExpanded3}全部展开/折叠Button(全部展开).width(100%).margin({top:8}).onClick((){this.isExpanded1truethis.isExpanded2truethis.isExpanded3true})Button(全部折叠).width(100%).margin({top:6}).onClick((){this.isExpanded1falsethis.isExpanded2falsethis.isExpanded3false})踩坑总结坑1transition 不加 animation 参数不生效TransitionEffect后面必须跟.animation()来指定时长和曲线否则可能使用默认参数或者直接没有动画效果。坑2Builder 里的状态响应Builder方法内部访问State变量是可以正常响应变化的但前提是这些变量属于调用Builder的组件本身。如果Builder被另一个组件调用状态响应可能会有问题。坑3折叠时内容闪烁有时候折叠动画开始的一瞬间内容会闪一下。这通常是因为内容区域的高度变化太快。解决办法是给外层容器也加上.animation().animation({duration:300,curve:Curve.EaseInOut})写在最后展开折叠动画的关键就是TransitionEffect.asymmetric——让出现和消失用不同的动画效果。这个小技巧能让交互变得更自然因为现实世界中展开和折叠本来就不是完全对称的动作。如果你的 App 里有 FAQ、设置项、手风琴菜单这类场景不妨加上这个效果开发成本低但体验提升明显。