查看: 114|回复: 0

鸿蒙特效教程09-深入学习animateTo动画

[复制链接]

1

主题

3

回帖

16

积分

新手上路

积分
16
发表于 2025-3-31 08:57:19 | 显示全部楼层 |阅读模式
鸿蒙特效教程09-深入学习animateTo动画

本教程将带领大家从零开始,一步步讲解如何讲解 animateTo 动画,并实现按钮交互效果,使新手也能轻松掌握。
效果演示

通过两个常见的按钮动画效果,深入学习 HarmonyOS Next 的 animateTo 动画,以及探索最佳实践。
    缩放按钮效果抖动按钮效果

一、基础准备

1.1 理解ArkUI中的动画机制

HarmonyOS的ArkUI框架提供了强大的动画支持,常见有两种实现方式:
    声明式动画:通过 .animation()属性直接应用于组件命令式动画:通过 animateTo()方法动态改变状态触发动画
本文将主要使用 animateTo()方法,因为它更灵活,能实现更复杂的动画效果。
1.2 创建基础项目结构

首先,我们创建一个基本的页面组件结构:
  1. @Entry
  2. @Component
  3. struct ButtonAnimation {
  4.   // 状态变量将在后续步骤中添加
  5.   build() {
  6.     Column({ space: 20 }) {
  7.       Text('按钮交互效果')
  8.         .fontSize(22)
  9.         .fontWeight(FontWeight.Bold)
  10.       // 后续步骤将在这里添加按钮组件
  11.     }
  12.     .width('100%')
  13.     .height('100%')
  14.     .padding(20)
  15.     .backgroundColor('#ffb3d0ff')
  16.     .justifyContent(FlexAlign.Center)
  17.     .expandSafeArea()
  18.   }
  19. }
复制代码
这段代码创建了一个基本的页面布局,包含一个标题文本。接下来,我们将逐步添加按钮和动画效果。
二、实现按钮点击缩放效果

2.1 添加基础按钮布局

首先,添加一个按钮及其容器:
  1. // 按钮缩放效果
  2. Column({ space: 10 }) {
  3.   Text('按钮点击缩放效果')
  4.     .fontSize(16)
  5.     .fontWeight(FontWeight.Medium)
  6.   Button('点击缩放')
  7.     .width(150)
  8.     .fontSize(16)
  9.     // 动画相关属性将在后续步骤添加
  10.     .onClick(() => {
  11.       // 点击处理函数将在后续步骤添加
  12.       console.log('按钮被点击了')
  13.     })
  14. }
  15. .padding(16)
  16. .borderRadius(12)
  17. .backgroundColor('#F0F5FF')
  18. .width('100%')
  19. .margin({ top: 16 })
  20. .alignItems(HorizontalAlign.Center)
复制代码
这段代码添加了一个带标题的按钮区域,并为按钮设置了基本样式。
2.2 添加状态变量和缩放属性

要实现缩放效果,我们需要添加一个状态变量来控制按钮的缩放比例:
  1. @State buttonScale: number = 1.0
复制代码
然后,为按钮添加缩放属性:
  1. Button('点击缩放')
  2.   .width(150)
  3.   .fontSize(16)
  4.   .scale({ x: this.buttonScale, y: this.buttonScale })  // 添加缩放属性
  5.   .onClick(() => {
  6.     console.log('按钮被点击了')
  7.   })
复制代码
.scale()属性用于设置组件的缩放比例,通过改变 buttonScale的值,可以实现按钮的缩放效果。
2.3 实现简单的缩放动画

现在,添加一个简单的点击缩放效果:
  1. // 按钮点击缩放效果
  2. pressButton() {
  3.   // 缩小
  4.   animateTo({
  5.     duration: 100,  // 动画持续时间(毫秒)
  6.     curve: Curve.EaseIn  // 缓动曲线
  7.   }, () => {
  8.     this.buttonScale = 0.9  // 缩小到90%
  9.   })
  10.   // 延时后恢复原大小
  11.   setTimeout(() => {
  12.     animateTo({
  13.       duration: 200,
  14.       curve: Curve.EaseOut
  15.     }, () => {
  16.       this.buttonScale = 1.0  // 恢复原大小
  17.     })
  18.   }, 100)
  19. }
复制代码
然后修改按钮的点击处理函数:
  1. .onClick(() => {
  2.   this.pressButton()  // 调用缩放动画函数
  3.   console.log('按钮被点击了')
  4. })
复制代码
这段代码实现了一个基本的缩放动画:按钮点击时先缩小到90%,然后恢复原大小。但是它使用了 setTimeout,我们可以进一步优化。
2.4 使用onFinish回调优化动画

animateTo()方法提供了 onFinish回调,可以在动画完成后执行操作。我们可以使用它来替代 setTimeout:
  1. @State animationCount: number = 0  // 用于跟踪动画状态
  2. // 按钮点击缩放效果
  3. pressButton() {
  4.   this.animationCount = 0
  5.   // 缩小
  6.   animateTo({
  7.     duration: 100,
  8.     curve: Curve.EaseIn,  // 缓入曲线
  9.     onFinish: () => {
  10.       // 动画完成后立即开始第二阶段
  11.       animateTo({
  12.         duration: 200,
  13.         curve: Curve.ExtremeDeceleration  // 急缓曲线
  14.       }, () => {
  15.         this.buttonScale = 1.0
  16.       })
  17.     }
  18.   }, () => {
  19.     this.animationCount++
  20.     this.buttonScale = 0.9
  21.   })
  22. }
复制代码
这种实现方式更加优雅,没有使用 setTimeout,而是利用动画完成回调来链接多个动画阶段。此外,我们使用了不同的缓动曲线,使动画更加生动:
    Curve.EaseIn:缓入曲线,动画开始时缓慢,然后加速Curve.ExtremeDeceleration:急缓曲线,开始快速然后迅速减慢,产生弹性的视觉效果
三、实现按钮抖动效果

3.1 添加抖动按钮布局

先添加抖动按钮的UI部分:
  1. // 抖动效果
  2. Column({ space: 10 }) {
  3.   Text('按钮抖动效果')
  4.     .fontSize(16)
  5.     .fontWeight(FontWeight.Medium)
  6.   Button('点击抖动')
  7.     .width(150)
  8.     .fontSize(16)
  9.     // 动画相关属性将在后续步骤添加
  10.     .onClick(() => {
  11.       console.log('按钮被点击了')
  12.     })
  13. }
  14. .padding(16)
  15. .borderRadius(12)
  16. .backgroundColor('#F0F5FF')
  17. .width('100%')
  18. .alignItems(HorizontalAlign.Center)
复制代码
3.2 添加状态变量和位移属性

要实现抖动效果,我们需要添加状态变量来控制按钮的水平位移:
  1. @State shakeOffset: number = 0  // 控制水平抖动偏移
  2. @State shakeStep: number = 0    // 用于跟踪抖动步骤
复制代码
然后,为按钮添加平移属性:
  1. Button('点击抖动')
  2.   .width(150)
  3.   .fontSize(16)
  4.   .translate({ x: this.shakeOffset })  // 添加水平平移属性
  5.   .onClick(() => {
  6.     console.log('按钮被点击了')
  7.   })
复制代码
.translate()属性用于设置组件的平移,通过改变 shakeOffset的值,我们可以让按钮左右移动。
3.3 使用setTimeout实现连续抖动

一个简单的实现方式是使用多个 setTimeout来创建连续的抖动:
  1. // 抖动效果
  2. startShake() {
  3.   // 向右移动
  4.   animateTo({ duration: 50 }, () => {
  5.     this.shakeOffset = 5
  6.   })
  7.   // 向左移动
  8.   setTimeout(() => {
  9.     animateTo({ duration: 50 }, () => {
  10.       this.shakeOffset = -5
  11.     })
  12.   }, 50)
  13.   // 向右小幅移动
  14.   setTimeout(() => {
  15.     animateTo({ duration: 50 }, () => {
  16.       this.shakeOffset = 3
  17.     })
  18.   }, 100)
  19.   // 向左小幅移动
  20.   setTimeout(() => {
  21.     animateTo({ duration: 50 }, () => {
  22.       this.shakeOffset = -3
  23.     })
  24.   }, 150)
  25.   // 回到中心
  26.   setTimeout(() => {
  27.     animateTo({ duration: 50 }, () => {
  28.       this.shakeOffset = 0
  29.     })
  30.   }, 200)
  31. }
复制代码
修改按钮的点击处理函数:
  1. .onClick(() => {
  2.   this.startShake()  // 调用抖动动画函数
  3.   console.log('按钮被点击了')
  4. })
复制代码
这段代码通过多个 setTimeout连续改变按钮的水平偏移量,实现抖动效果。但是使用这么多的 setTimeout不够优雅,我们可以进一步优化。
3.4 使用递归和onFinish回调优化抖动动画

我们可以使用递归和 onFinish回调来替代多个 setTimeout,使代码更加优雅:
  1. // 抖动效果
  2. startShake() {
  3.   this.shakeStep = 0
  4.   this.executeShakeStep()
  5. }
  6. // 执行抖动的每一步
  7. executeShakeStep() {
  8.   const shakeValues = [5, -5, 3, -3, 0]  // 定义抖动序列
  9.   if (this.shakeStep >= shakeValues.length) {
  10.     return  // 所有步骤完成后退出
  11.   }
  12.   animateTo({
  13.     duration: 50,
  14.     curve: Curve.Linear,  // 匀速曲线
  15.     onFinish: () => {
  16.       this.shakeStep++
  17.       if (this.shakeStep < shakeValues.length) {
  18.         this.executeShakeStep()  // 递归执行下一步抖动
  19.       }
  20.     }
  21.   }, () => {
  22.     this.shakeOffset = shakeValues[this.shakeStep]  // 设置当前步骤的偏移值
  23.   })
  24. }
复制代码
这种实现方式更加优雅和灵活:
    使用数组 shakeValues定义整个抖动序列通过递归调用 executeShakeStep()和 onFinish回调,实现连续动画没有使用 setTimeout,使代码更加清晰和易于维护
四、animateTo API详解

animateTo()是HarmonyOS中实现动画的核心API,它的基本语法如下:
  1. animateTo(value: AnimateParam, event: () => void): void
复制代码
4.1 AnimateParam参数

AnimateParam是一个配置对象,包含以下主要属性:
    duration: number - 动画持续时间,单位为毫秒tempo: number - 动画播放速度,值越大动画播放越快,默认值 1curve: Curve - 动画的缓动曲线,控制动画的速度变化delay: number - 动画开始前的延迟时间,单位为毫秒iterations: number - 动画重复次数,-1表示无限循环playMode: PlayMode - 动画播放模式,如正向、反向、交替等onFinish: () => void - 动画完成时的回调函数
4.2 常用缓动曲线

HarmonyOS提供了多种缓动曲线,可以实现不同的动画效果:
    Curve.Linear: 线性曲线,动画速度恒定Curve.EaseIn: 缓入曲线,动画开始缓慢,然后加速Curve.EaseOut: 缓出曲线,动画开始快速,然后减速Curve.EaseInOut: 缓入缓出曲线,动画开始和结束都缓慢,中间快速Curve.FastOutSlowIn: 快出慢入曲线,类似于Android的标准曲线Curve.ExtremeDeceleration: 急缓曲线,用于模拟弹性效果curves.springMotion(): 弹簧曲线,模拟物理弹簧效果
4.3 动画函数

event是一个函数,在这个函数中改变状态变量的值,从而触发动画。例如:
  1. animateTo({ duration: 300 }, () => {
  2.   this.buttonScale = 0.9  // 改变状态变量,触发缩放动画
  3. })
复制代码
4.4 连续动画的实现方式

有几种方式可以实现连续的动画效果:
    使用setTimeout(不推荐):
  1. animateTo({ duration: 300 }, () => { this.value1 = newValue1 })
  2. setTimeout(() => {
  3.   animateTo({ duration: 300 }, () => { this.value2 = newValue2 })
  4. }, 300)
复制代码
    使用onFinish回调(推荐):
  1. animateTo({
  2.   duration: 300,
  3.   onFinish: () => {
  4.     animateTo({ duration: 300 }, () => { this.value2 = newValue2 })
  5.   }
  6. }, () => {
  7.   this.value1 = newValue1
  8. })
复制代码
    使用递归和计数器(用于复杂序列):
  1. let steps = [value1, value2, value3]
  2. let currentStep = 0
  3. function executeNextStep() {
  4.   if (currentStep >= steps.length) return
  5.   animateTo({
  6.     duration: 300,
  7.     onFinish: () => {
  8.       currentStep++
  9.       if (currentStep < steps.length) {
  10.         executeNextStep()
  11.       }
  12.     }
  13.   }, () => {
  14.     this.value = steps[currentStep]
  15.   })
  16. }
  17. executeNextStep()
复制代码
五、完整代码实现

下面是完整的按钮动画效果实现代码:
  1. @Entry
  2. @Component
  3. struct ButtonAnimation {
  4.   @State buttonScale: number = 1.0
  5.   @State shakeOffset: number = 0
  6.   @State animationCount: number = 0  // 用于跟踪动画状态
  7.   @State shakeStep: number = 0  // 用于跟踪抖动步骤
  8.   // 按钮点击缩放效果
  9.   pressButton() {
  10.     this.animationCount = 0
  11.     // 缩小
  12.     animateTo({
  13.       duration: 100,
  14.       curve: Curve.EaseIn,  // 缓入曲线
  15.       onFinish: () => {
  16.         // 动画完成后立即开始第二阶段
  17.         animateTo({
  18.           duration: 200,
  19.           curve: Curve.ExtremeDeceleration  // 急缓曲线
  20.         }, () => {
  21.           this.buttonScale = 1.0
  22.         })
  23.       }
  24.     }, () => {
  25.       this.animationCount++
  26.       this.buttonScale = 0.9
  27.     })
  28.   }
  29.   // 抖动效果
  30.   startShake() {
  31.     this.shakeStep = 0
  32.     this.executeShakeStep()
  33.   }
  34.   // 执行抖动的每一步
  35.   executeShakeStep() {
  36.     const shakeValues = [5, -5, 3, -3, 0]
  37.     if (this.shakeStep >= shakeValues.length) {
  38.       return
  39.     }
  40.     animateTo({
  41.       duration: 50,
  42.       curve: Curve.Linear,  // 匀速曲线
  43.       onFinish: () => {
  44.         this.shakeStep++
  45.         if (this.shakeStep < shakeValues.length) {
  46.           this.executeShakeStep()  // 递归执行下一步抖动
  47.         }
  48.       }
  49.     }, () => {
  50.       this.shakeOffset = shakeValues[this.shakeStep]
  51.     })
  52.   }
  53.   build() {
  54.     Column({ space: 20 }) {
  55.       Text('按钮交互效果')
  56.         .fontSize(22)
  57.         .fontWeight(FontWeight.Bold)
  58.       // 按钮缩放效果
  59.       Column({ space: 10 }) {
  60.         Text('按钮点击缩放效果')
  61.           .fontSize(16)
  62.           .fontWeight(FontWeight.Medium)
  63.         Button('点击缩放')
  64.           .width(150)
  65.           .fontSize(16)
  66.           .scale({ x: this.buttonScale, y: this.buttonScale })
  67.           .onClick(() => {
  68.             // 缩放效果
  69.             this.pressButton()
  70.             // 你的业务逻辑
  71.             console.log('你的业务逻辑')
  72.           })
  73.       }
  74.       .padding(16)
  75.       .borderRadius(12)
  76.       .backgroundColor('#F0F5FF')
  77.       .width('100%')
  78.       .margin({ top: 16 })
  79.       .alignItems(HorizontalAlign.Center)
  80.       // 抖动效果
  81.       Column({ space: 10 }) {
  82.         Text('按钮抖动效果')
  83.           .fontSize(16)
  84.           .fontWeight(FontWeight.Medium)
  85.         Button('点击抖动')
  86.           .width(150)
  87.           .fontSize(16)
  88.           .translate({ x: this.shakeOffset })
  89.           .onClick(() => {
  90.             // 你的业务逻辑
  91.             console.log('你的业务逻辑')
  92.             // 模拟轻微震动反馈,适用于错误提示或注意力引导
  93.             this.startShake()
  94.           })
  95.       }
  96.       .padding(16)
  97.       .borderRadius(12)
  98.       .backgroundColor('#F0F5FF')
  99.       .width('100%')
  100.       .alignItems(HorizontalAlign.Center)
  101.     }
  102.     .width('100%')
  103.     .height('100%')
  104.     .padding(20)
  105.     .backgroundColor('#ffb3d0ff')
  106.     .justifyContent(FlexAlign.Center)
  107.     .expandSafeArea()
  108.   }
  109. }
复制代码
六、应用场景和扩展

6.1 适用场景

    缩放效果:适用于提供用户点击反馈,增强交互感抖动效果:适用于错误提示、警告或引起用户注意
6.2 可能的扩展

    结合振动反馈:与设备振动结合,提供触觉反馈添加声音反馈:配合音效,提供听觉反馈组合多种动画:如缩放+旋转、缩放+颜色变化等
6.3 性能优化建议

    避免过于复杂的动画,尤其是在低端设备上合理选择动画持续时间,一般不超过300ms对于频繁触发的动画,考虑增加防抖处理使用 onFinish回调代替 setTimeout实现连续动画
七、总结与心得

通过本文,我们学习了如何在HarmonyOS中实现按钮缩放和抖动效果,关键点包括:
    使用 @State状态变量控制动画参数利用 animateTo()方法实现流畅的状态变化动画选择合适的缓动曲线让动画更加自然使用 onFinish回调和递归实现连续动画,避免使用 setTimeout将动画逻辑封装为独立方法,使代码更加清晰
动画效果能够显著提升应用的用户体验,希望本文能帮助你在HarmonyOS应用中添加生动、自然的交互动画。随着你对 animateTo() API的深入理解,可以创造出更加复杂和精美的动画效果。
希望这篇 HarmonyOS Next 教程对你有所帮助,期待您的点赞、评论、收藏。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表