公司动态
HarmonyOS 应用开发《掌上英语》第19篇:3D 翻转动画实现——ArkTS 动画系统全解析
3D 翻转动画实现——ArkTS 动画系统全解析引言在移动应用中卡片翻转动画是最受欢迎的交互动效之一它能给用户带来直观的物理世界操作感。在我们的英语学习 App 的单词学习页面CourseHomePage.ets中就实现了一个完整的 3D 卡片翻转效果用户点击卡片正面单词翻转显示背面释义仿佛在翻动一张真实的学习卡片。本文将深入剖析这个动画的实现原理并系统讲解 ArkTS 的动画 API。一、动画系统概览ArkTS 提供了两套动画 APIAPI类型使用场景animateTo()显式动画通过状态变化驱动动画.animation()属性动画链式调用跟随属性变化本项目中的卡片翻转使用的是animateTo显式动画因为它需要对多个属性rotateY、opacity进行同步控制。二、卡片翻转动画源码分析2.1 状态定义翻转动画涉及 4 个关键状态变量LocalisFlipped:booleanfalse;// 翻转状态标记LocalrotateY:number0;// Y 轴旋转角度LocalfrontOpacity:number1;// 正面透明度LocalbackOpacity:number0;// 反面透明度isFlipped布尔标记表示当前是否处于翻转状态rotateY驱动 3D 旋转的核心变量范围 0°~180°frontOpacity/backOpacity控制正反面可见性实现翻转过程中透明切换2.2 翻转方法privateflipCard():void{animateTo({duration:400,curve:Curve.EaseInOut},(){if(this.isFlipped){// 翻回正面this.rotateY0;this.frontOpacity1;this.backOpacity0;}else{// 翻到反面this.rotateY180;this.frontOpacity0;this.backOpacity1;}this.isFlipped!this.isFlipped;});}动画工作流程用户点击卡片 → flipCard() 被调用 → animateTo() 启动动画闭包 → 框架检测到 rotateY/frontOpacity/backOpacity 变化 → 在 400ms 内使用 EaseInOut 曲线平滑过渡 → 动画完成后isFlipped 状态取反2.3 UI 层实现正反两面卡片使用Stack容器叠放通过rotate和opacity控制显示Stack(){// 正面卡片 Column(){Row(){Text(this.currentWord().partOfSpeech).fontSize(12).fontColor(#165DFF).backgroundColor(#EAF2FF).padding({left:10,right:10,top:4,bottom:4}).borderRadius(12);Blank();Text(this.isFavorite?★:☆).fontSize(24).fontColor(#FFB020);}.width(100%).padding({left:18,right:18,top:14});Column(){Text(this.currentWord().word).fontSize(34).fontWeight(FontWeight.Bold).fontColor(#165DFF).margin({bottom:8}).onClick(()this.playPronunciation());Row({space:8}){Text(this.currentWord().phonetic).fontSize(15).fontColor($r(sys.color.font_secondary));Text(▶).fontSize(12).fontColor(#FFFFFF).width(26).height(26).textAlign(TextAlign.Center).borderRadius(13).backgroundColor(#165DFF);}.alignItems(VerticalAlign.Center).margin({bottom:16});Text(this.currentWord().translation).fontSize(20).fontWeight(FontWeight.Medium).fontColor($r(sys.color.font_primary)).textAlign(TextAlign.Center).margin({bottom:14});Text(this.currentWord().example).fontSize(14).fontColor($r(sys.color.font_secondary)).textAlign(TextAlign.Center).maxLines(2).padding({left:18,right:18});}.layoutWeight(1).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center).width(100%);Text(点击卡片查看释义).fontSize(11).fontColor($r(sys.color.font_tertiary)).margin({bottom:12});}.width(100%).height(280).backgroundColor($r(sys.color.background_primary)).borderRadius(20).shadow(ShadowStyle.OUTER_DEFAULT_MD)// 3D 旋转设置.rotate({x:0,y:this.rotateY,z:0,centerX:50%,centerY:50%,perspective:1200}).opacity(this.frontOpacity).onClick(()this.flipCard());// 反面卡片 Column(){Text(详细释义).fontSize(13).fontColor($r(sys.color.font_secondary)).margin({bottom:8});Text(this.currentWord().translation).fontSize(22).fontWeight(FontWeight.Bold).fontColor(#165DFF).textAlign(TextAlign.Center).margin({bottom:14});Text(this.currentWord().example).fontSize(14).fontColor($r(sys.color.font_primary)).textAlign(TextAlign.Center).maxLines(2).margin({bottom:8});Text(this.currentWord().exampleTranslation).fontSize(13).fontColor($r(sys.color.font_secondary)).textAlign(TextAlign.Center).maxLines(2).margin({bottom:14});Row({space:6}){Text(this.getCategoryLabel()).fontSize(10).fontColor(#165DFF).backgroundColor(#EAF2FF).padding({left:8,right:8,top:3,bottom:3}).borderRadius(8);ForEach(this.currentWord().tags,(tag:string){Text(tag).fontSize(10).fontColor(#165DFF).backgroundColor(#EAF2FF).padding({left:8,right:8,top:3,bottom:3}).borderRadius(8);},(tag:string)tag);}.justifyContent(FlexAlign.Center).margin({bottom:10});Text(点击返回正面).fontSize(11).fontColor($r(sys.color.font_tertiary));}.width(100%).height(280).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center).backgroundColor(#F8FAFC).borderRadius(20).shadow(ShadowStyle.OUTER_DEFAULT_MD)// 反面旋转比正面始终少 180°实现背面跟随.rotate({x:0,y:this.rotateY-180,z:0,centerX:50%,centerY:50%,perspective:1200}).opacity(this.backOpacity).onClick(()this.flipCard());}三、3D 旋转的核心参数3.1 rotate 属性详解rotate属性是 ArkTS 实现 3D 变换的关键它包含 5 个参数参数说明本项目中取值xX 轴旋转分量0不绕 X 轴旋转yY 轴旋转分量this.rotateY动态变化 0→180zZ 轴旋转分量0不绕 Z 轴旋转centerX旋转中心 X50%卡片中心centerY旋转中心 Y50%卡片中心perspective透视距离1200单位 vp3.2 反面旋转的特殊处理反面卡片的旋转角度为this.rotateY - 180// 正面.rotate({x:0,y:this.rotateY,...})// 反面比正面始终少 180°.rotate({x:0,y:this.rotateY-180,...})为什么要这样做当rotateY 0正面朝前反面 0 - 180 -180°即反面已经转了 180°处于不可见状态当rotateY 180翻转完成反面 180 - 180 0°即反面转回 0°处于可见状态在动画过程中正反面同步旋转始终保持背对背的关系3.3 perspective 透视效果perspective: 1200设定了透视距离值越小3D 效果越强但过小会导致变形严重perspective 800 → 3D 效果强烈近大远小明显 perspective 1200 → 舒适的自然 3D 效果 ✓ perspective 2000 → 3D 效果减弱接近平面旋转四、opacity 与 visibility 的动画协调4.1 正反面透明度切换翻转过程中正反面透明度的同步变化至关重要动画阶段rotateYfrontOpacitybackOpacity视觉效果起始正面0°10正面完全可见旋转到 90°90°渐变中…渐变中…开始看到反面翻转完成180°01反面完全可见4.2 为什么不直接用 visibility可能有人会问为什么不直接用visibility控制正反面显示// ❌ 不推荐visibility 无法参与动画过渡.visibility(this.isFlipped?Visibility.Hidden:Visibility.Visible)原因在于visibility是离散状态显示/隐藏不能产生平滑的过渡动画。而opacity是连续数值0→1可以自然地产生渐隐渐现效果。4.3 完整的动画时序时间轴: 0ms ---------- 200ms ---------- 400ms ↓ ↓ ↓ rotateY: 0° →→→→→→→→→→ 90° →→→→→→→→→→ 180° front: 1.0 →→→→→→→→→ 0.5 →→→→→→→→→→ 0.0 back: 0.0 →→→→→→→→→ 0.5 →→→→→→→→→→ 1.0 视觉效果正面卡片沿 Y 轴旋转到 90° 时正反面各半 继续旋转到 180° 时反面完全显示五、animateTo API 深入5.1 参数配置animateTo({duration:400,// 动画时长单位 mscurve:Curve.EaseInOut,// 动画曲线delay:0,// 延迟启动单位 msiterations:1,// 播放次数playMode:PlayMode.Normal,// 播放模式onFinish:(){}// 动画完成回调},(){// 闭包内修改变量框架自动侦测并驱动动画this.rotateY180;});5.2 动画曲线选择曲线效果适用场景Curve.EaseInOut慢→快→慢✅ 翻转动画Curve.Ease快→慢元素弹出Curve.Linear匀速进度条Curve.FastOutSlowIn快→慢Material Design 风格EaseInOut是翻转动画的理想选择——它让卡片在起始和结束时速度较慢模拟了真实世界中卡片翻转的物理感受。六、最佳实践6.1 动画性能优化只动画化 transform 和 opacity这两个属性的动画可以由 GPU 硬件加速避免动画化width、height等触发布局重排的属性合理设置 perspective800-1500是视觉效果和性能的平衡区间控制动画时长300-500ms是翻转动画的黄金区间过短显得生硬过长则显拖沓6.2 重置动画状态在切换单词时需要重置所有动画状态privatenextCard():void{if(this.currentIndexthis.totalCount-1){// 重置翻转状态this.isFlippedfalse;this.rotateY0;this.frontOpacity1;this.backOpacity0;this.isFavoritefalse;this.currentIndex;}}6.3 在 Tab 切换时重置当用户切换单词分类 Tab 时也要重置动画状态避免卡片处于卡在半路的状态.onTabBarClick((index:number){// ...this.currentIndex0;this.isFlippedfalse;this.frontOpacity1;this.backOpacity0;this.rotateY0;this.isFavoritefalse;})七、扩展从 Y 轴翻转到 X/Z 轴旋转如果你想让卡片实现不同的翻转方向只需调整rotate的轴向参数// X 轴翻转上下翻.rotate({x:this.rotateX,y:0,z:0,...})// Z 轴旋转平面旋转.rotate({x:0,y:0,z:this.rotateZ,...})// 组合翻转3D 空间旋转.rotate({x:this.rotateX,y:this.rotateY,z:0,...})总结通过CourseHomePage.ets中的 3D 翻转动画实现我们深入学习了 ArkTS 动画系统的核心机制animateTo()显式动画在闭包中修改状态变量框架自动驱动动画rotate3D 变换通过 Y 轴旋转 perspective实现真实 3D 翻转opacity动画协调正反面透明度同步变化实现自然过渡EaseInOut曲线模拟物理世界的缓动效果这个翻转动画虽然代码量不大但涉及了 ArkTS 动画系统的多个核心概念是学习动画开发的绝佳案例。