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

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

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

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

开发环境准备

  • DevEco Studio 5.0.3
  • HarmonyOS Next API 15

下载代码仓库

效果演示

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

  • 缩放按钮效果
  • 抖动按钮效果

特效09-按钮交互-效果.gif

一、基础准备

1.1 理解ArkUI中的动画机制

HarmonyOS的ArkUI框架提供了强大的动画支持,常见有两种实现方式:

  • 声明式动画:通过.animation()属性直接应用于组件
  • 命令式动画:通过animateTo()方法动态改变状态触发动画

本文将主要使用animateTo()方法,因为它更灵活,能实现更复杂的动画效果。

1.2 创建基础项目结构

首先,我们创建一个基本的页面组件结构:

@Entry
@Component
struct ButtonAnimation {
   
  // 状态变量将在后续步骤中添加

  build() {
   
    Column({
    space: 20 }) {
   
      Text('按钮交互效果')
        .fontSize(22)
        .fontWeight(FontWeight.Bold)

      // 后续步骤将在这里添加按钮组件
    }
    .width('100%')
    .height('100%')
    .padding(20)
    .backgroundColor('#ffb3d0ff')
    .justifyContent(FlexAlign.Center)
    .expandSafeArea()
  }
}

这段代码创建了一个基本的页面布局,包含一个标题文本。接下来,我们将逐步添加按钮和动画效果。

二、实现按钮点击缩放效果

2.1 添加基础按钮布局

首先,添加一个按钮及其容器:

// 按钮缩放效果
Column({
    space: 10 }) {
   
  Text('按钮点击缩放效果')
    .fontSize(16)
    .fontWeight(FontWeight.Medium)

  Button('点击缩放')
    .width(150)
    .fontSize(16)
    // 动画相关属性将在后续步骤添加
    .onClick(() => {
   
      // 点击处理函数将在后续步骤添加
      console.log('按钮被点击了')
    })
}
.padding(16)
.borderRadius(12)
.backgroundColor('#F0F5FF')
.width('100%')
.margin({
    top: 16 })
.alignItems(HorizontalAlign.Center)

这段代码添加了一个带标题的按钮区域,并为按钮设置了基本样式。

2.2 添加状态变量和缩放属性

要实现缩放效果,我们需要添加一个状态变量来控制按钮的缩放比例:

@State buttonScale: number = 1.0

然后,为按钮添加缩放属性:

Button('点击缩放')
  .width(150)
  .fontSize(16)
  .scale({
    x: this.buttonScale, y: this.buttonScale })  // 添加缩放属性
  .onClick(() => {
   
    console.log('按钮被点击了')
  })

.scale()属性用于设置组件的缩放比例,通过改变buttonScale的值,可以实现按钮的缩放效果。

2.3 实现简单的缩放动画

现在,添加一个简单的点击缩放效果:

// 按钮点击缩放效果
pressButton() {
   
  // 缩小
  animateTo({
   
    duration: 100,  // 动画持续时间(毫秒)
    curve: Curve.EaseIn  // 缓动曲线
  }, () => {
   
    this.buttonScale = 0.9  // 缩小到90%
  })

  // 延时后恢复原大小
  setTimeout(() => {
   
    animateTo({
   
      duration: 200,
      curve: Curve.EaseOut
    }, () => {
   
      this.buttonScale = 1.0  // 恢复原大小
    })
  }, 100)
}

然后修改按钮的点击处理函数:

.onClick(() => {
   
  this.pressButton()  // 调用缩放动画函数
  console.log('按钮被点击了')
})

这段代码实现了一个基本的缩放动画:按钮点击时先缩小到90%,然后恢复原大小。但是它使用了setTimeout,我们可以进一步优化。

2.4 使用onFinish回调优化动画

animateTo()方法提供了onFinish回调,可以在动画完成后执行操作。我们可以使用它来替代setTimeout

@State animationCount: number = 0  // 用于跟踪动画状态

// 按钮点击缩放效果
pressButton() {
   
  this.animationCount = 0
  // 缩小
  animateTo({
   
    duration: 100,
    curve: Curve.EaseIn,  // 缓入曲线
    onFinish: () => {
   
      // 动画完成后立即开始第二阶段
      animateTo({
   
        duration: 200,
        curve: Curve.ExtremeDeceleration  // 急缓曲线
      }, () => {
   
        this.buttonScale = 1.0
      })
    }
  }, () => {
   
    this.animationCount++
    this.buttonScale = 0.9
  })
}

这种实现方式更加优雅,没有使用setTimeout,而是利用动画完成回调来链接多个动画阶段。此外,我们使用了不同的缓动曲线,使动画更加生动:

  • Curve.EaseIn:缓入曲线,动画开始时缓慢,然后加速
  • Curve.ExtremeDeceleration:急缓曲线,开始快速然后迅速减慢,产生弹性的视觉效果

三、实现按钮抖动效果

3.1 添加抖动按钮布局

先添加抖动按钮的UI部分:

// 抖动效果
Column({
    space: 10 }) {
   
  Text('按钮抖动效果')
    .fontSize(16)
    .fontWeight(FontWeight.Medium)

  Button('点击抖动')
    .width(150)
    .fontSize(16)
    // 动画相关属性将在后续步骤添加
    .onClick(() => {
   
      console.log('按钮被点击了')
    })
}
.padding(16)
.borderRadius(12)
.backgroundColor('#F0F5FF')
.width('100%')
.alignItems(HorizontalAlign.Center)

3.2 添加状态变量和位移属性

要实现抖动效果,我们需要添加状态变量来控制按钮的水平位移:

@State shakeOffset: number = 0  // 控制水平抖动偏移
@State shakeStep: number = 0    // 用于跟踪抖动步骤

然后,为按钮添加平移属性:

Button('点击抖动')
  .width(150)
  .fontSize(16)
  .translate({
    x: this.shakeOffset })  // 添加水平平移属性
  .onClick(() => {
   
    console.log('按钮被点击了')
  })

.translate()属性用于设置组件的平移,通过改变shakeOffset的值,我们可以让按钮左右移动。

3.3 使用setTimeout实现连续抖动

一个简单的实现方式是使用多个setTimeout来创建连续的抖动:

// 抖动效果
startShake() {
   
  // 向右移动
  animateTo({
    duration: 50 }, () => {
   
    this.shakeOffset = 5
  })

  // 向左移动
  setTimeout(() => {
   
    animateTo({
    duration: 50 }, () => {
   
      this.shakeOffset = -5
    })
  }, 50)

  // 向右小幅移动
  setTimeout(() => {
   
    animateTo({
    duration: 50 }, () => {
   
      this.shakeOffset = 3
    })
  }, 100)

  // 向左小幅移动
  setTimeout(() => {
   
    animateTo({
    duration: 50 }, () => {
   
      this.shakeOffset = -3
    })
  }, 150)

  // 回到中心
  setTimeout(() => {
   
    animateTo({
    duration: 50 }, () => {
   
      this.shakeOffset = 0
    })
  }, 200)
}

修改按钮的点击处理函数:

.onClick(() => {
   
  this.startShake()  // 调用抖动动画函数
  console.log('按钮被点击了')
})

这段代码通过多个setTimeout连续改变按钮的水平偏移量,实现抖动效果。但是使用这么多的setTimeout不够优雅,我们可以进一步优化。

3.4 使用递归和onFinish回调优化抖动动画

我们可以使用递归和onFinish回调来替代多个setTimeout,使代码更加优雅:

// 抖动效果
startShake() {
   
  this.shakeStep = 0
  this.executeShakeStep()
}

// 执行抖动的每一步
executeShakeStep() {
   
  const shakeValues = [5, -5, 3, -3, 0]  // 定义抖动序列

  if (this.shakeStep >= shakeValues.length) {
   
    return  // 所有步骤完成后退出
  }

  animateTo({
   
    duration: 50,
    curve: Curve.Linear,  // 匀速曲线
    onFinish: () => {
   
      this.shakeStep++
      if (this.shakeStep < shakeValues.length) {
   
        this.executeShakeStep()  // 递归执行下一步抖动
      }
    }
  }, () => {
   
    this.shakeOffset = shakeValues[this.shakeStep]  // 设置当前步骤的偏移值
  })
}

这种实现方式更加优雅和灵活:

  1. 使用数组shakeValues定义整个抖动序列
  2. 通过递归调用executeShakeStep()onFinish回调,实现连续动画
  3. 没有使用setTimeout,使代码更加清晰和易于维护

四、animateTo API详解

animateTo()是HarmonyOS中实现动画的核心API,它的基本语法如下:

animateTo(value: AnimateParam, event: () => void): void

4.1 AnimateParam参数

AnimateParam是一个配置对象,包含以下主要属性:

  • duration: number - 动画持续时间,单位为毫秒
  • tempo: number - 动画播放速度,值越大动画播放越快,默认值 1
  • curve: 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是一个函数,在这个函数中改变状态变量的值,从而触发动画。例如:

animateTo({
    duration: 300 }, () => {
   
  this.buttonScale = 0.9  // 改变状态变量,触发缩放动画
})

4.4 连续动画的实现方式

有几种方式可以实现连续的动画效果:

  1. 使用setTimeout(不推荐):

    animateTo({
          duration: 300 }, () => {
          this.value1 = newValue1 })
    setTimeout(() => {
         
      animateTo({
          duration: 300 }, () => {
          this.value2 = newValue2 })
    }, 300)
    
  2. 使用onFinish回调(推荐):

    animateTo({
         
      duration: 300,
      onFinish: () => {
         
        animateTo({
          duration: 300 }, () => {
          this.value2 = newValue2 })
      }
    }, () => {
         
      this.value1 = newValue1
    })
    
  3. 使用递归和计数器(用于复杂序列):

    let steps = [value1, value2, value3]
    let currentStep = 0
    
    function executeNextStep() {
         
      if (currentStep >= steps.length) return
    
      animateTo({
         
        duration: 300,
        onFinish: () => {
         
          currentStep++
          if (currentStep < steps.length) {
         
            executeNextStep()
          }
        }
      }, () => {
         
        this.value = steps[currentStep]
      })
    }
    
    executeNextStep()
    

五、完整代码实现

下面是完整的按钮动画效果实现代码:

@Entry
@Component
struct ButtonAnimation {
   
  @State buttonScale: number = 1.0
  @State shakeOffset: number = 0
  @State animationCount: number = 0  // 用于跟踪动画状态
  @State shakeStep: number = 0  // 用于跟踪抖动步骤

  // 按钮点击缩放效果
  pressButton() {
   
    this.animationCount = 0
    // 缩小
    animateTo({
   
      duration: 100,
      curve: Curve.EaseIn,  // 缓入曲线
      onFinish: () => {
   
        // 动画完成后立即开始第二阶段
        animateTo({
   
          duration: 200,
          curve: Curve.ExtremeDeceleration  // 急缓曲线
        }, () => {
   
          this.buttonScale = 1.0
        })
      }
    }, () => {
   
      this.animationCount++
      this.buttonScale = 0.9
    })
  }

  // 抖动效果
  startShake() {
   
    this.shakeStep = 0
    this.executeShakeStep()
  }

  // 执行抖动的每一步
  executeShakeStep() {
   
    const shakeValues = [5, -5, 3, -3, 0]

    if (this.shakeStep >= shakeValues.length) {
   
      return
    }

    animateTo({
   
      duration: 50,
      curve: Curve.Linear,  // 匀速曲线
      onFinish: () => {
   
        this.shakeStep++
        if (this.shakeStep < shakeValues.length) {
   
          this.executeShakeStep()  // 递归执行下一步抖动
        }
      }
    }, () => {
   
      this.shakeOffset = shakeValues[this.shakeStep]
    })
  }

  build() {
   
    Column({
    space: 20 }) {
   
      Text('按钮交互效果')
        .fontSize(22)
        .fontWeight(FontWeight.Bold)

      // 按钮缩放效果
      Column({
    space: 10 }) {
   
        Text('按钮点击缩放效果')
          .fontSize(16)
          .fontWeight(FontWeight.Medium)

        Button('点击缩放')
          .width(150)
          .fontSize(16)
          .scale({
    x: this.buttonScale, y: this.buttonScale })
          .onClick(() => {
   
            // 缩放效果
            this.pressButton()
            // 你的业务逻辑
            console.log('你的业务逻辑')
          })
      }
      .padding(16)
      .borderRadius(12)
      .backgroundColor('#F0F5FF')
      .width('100%')
      .margin({
    top: 16 })
      .alignItems(HorizontalAlign.Center)

      // 抖动效果
      Column({
    space: 10 }) {
   
        Text('按钮抖动效果')
          .fontSize(16)
          .fontWeight(FontWeight.Medium)

        Button('点击抖动')
          .width(150)
          .fontSize(16)
          .translate({
    x: this.shakeOffset })
          .onClick(() => {
   
            // 你的业务逻辑
            console.log('你的业务逻辑')
            // 模拟轻微震动反馈,适用于错误提示或注意力引导
            this.startShake()
          })
      }
      .padding(16)
      .borderRadius(12)
      .backgroundColor('#F0F5FF')
      .width('100%')
      .alignItems(HorizontalAlign.Center)
    }
    .width('100%')
    .height('100%')
    .padding(20)
    .backgroundColor('#ffb3d0ff')
    .justifyContent(FlexAlign.Center)
    .expandSafeArea()
  }
}

六、应用场景和扩展

6.1 适用场景

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

6.2 可能的扩展

  1. 结合振动反馈:与设备振动结合,提供触觉反馈
  2. 添加声音反馈:配合音效,提供听觉反馈
  3. 组合多种动画:如缩放+旋转、缩放+颜色变化等

6.3 性能优化建议

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

七、总结与心得

通过本文,我们学习了如何在HarmonyOS中实现按钮缩放和抖动效果,关键点包括:

  1. 使用@State状态变量控制动画参数
  2. 利用animateTo()方法实现流畅的状态变化动画
  3. 选择合适的缓动曲线让动画更加自然
  4. 使用onFinish回调和递归实现连续动画,避免使用setTimeout
  5. 将动画逻辑封装为独立方法,使代码更加清晰

动画效果能够显著提升应用的用户体验,希望本文能帮助你在HarmonyOS应用中添加生动、自然的交互动画。随着你对 animateTo() API的深入理解,可以创造出更加复杂和精美的动画效果。

希望这篇 HarmonyOS Next 教程对你有所帮助,期待您的点赞、评论、收藏。

目录
相关文章
|
3月前
|
索引 容器
120. [HarmonyOS NEXT 实战案例:教育应用] 基础篇 - 垂直分割布局打造课程学习平台
在本案例中,我们将使用以下HarmonyOS NEXT组件: | 组件名称 | 功能描述 | | ------------- | -------------------------------------- | | `ColumnSplit` | 垂直分割布局容器,将界面分为上下两部分 | | `Column` | 垂直布局容器,用于垂直排列子组件 | | `Row` | 水平布局容器,用于水平排列子组件 | | `Scroll` | 滚动容器,用于在有限空间内展
71 3
120. [HarmonyOS NEXT 实战案例:教育应用] 基础篇 - 垂直分割布局打造课程学习平台
|
3月前
|
UED 容器
122.[HarmonyOS NEXT 实战案例:教育应用] 高级篇 - 课程学习平台的高级布局与自适应设计
在前两篇教程中,我们学习了如何使用HarmonyOS NEXT的`ColumnSplit`组件构建课程学习平台的基本布局,以及如何添加交互功能和状态管理。本篇教程将进一步深入,讲解课程学习平台的高级布局技巧和自适应设计,使应用能够在不同尺寸的设备上提供一致且优质的用户体验。
72 1
122.[HarmonyOS NEXT 实战案例:教育应用] 高级篇 - 课程学习平台的高级布局与自适应设计
|
3月前
|
索引
鸿蒙开发:自定义切换动画实现Swiper层叠滑动效果
customContentTransition不仅仅可以实现平移上的改变,很多的效果,我们都可以实现,比如放大缩小,旋转等等。
163 2
鸿蒙开发:自定义切换动画实现Swiper层叠滑动效果
|
4月前
鸿蒙开发:平移动画时间为啥没了?
问题的原因,第一个,由于键值发生了变化,造成了组件重新创建,第二个,由于组件重新创建,动画时机过早,造成属性未生效。
鸿蒙开发:平移动画时间为啥没了?
|
3月前
|
存储 API 容器
47.[HarmonyOS NEXT RelativeContainer案例四] 构建动态进度条:相对布局实现精确控制与动画效果
进度条是应用程序中常见的UI元素,用于直观地展示任务完成进度或数据加载状态。在HarmonyOS NEXT中,使用RelativeContainer可以轻松实现功能丰富、视觉精美的进度条。本教程将详细讲解如何利用RelativeContainer的锚点系统构建动态进度条,帮助你掌握这一实用技术。
84 5
|
4月前
|
安全 Go 开发工具
HarmonyOS5云服务技术分享--手机号登录教程
本文详细讲解了在HarmonyOS中集成手机号认证功能的全流程。首先分析了手机号认证的优势,如用户友好、安全性强及快速接入。接着介绍了环境准备步骤,包括集成AGC认证SDK、开启手机认证能力及添加必要权限。核心功能实现部分提供了新用户注册、密码登录和验证码登录的代码示例。此外,还涵盖了账号管理技巧,如修改绑定手机号、重置密码等,并提供了避坑指南和扩展能力建议,帮助开发者轻松实现安全高效的认证系统。
|
6月前
|
安全 UED 索引
鸿蒙特效教程06-可拖拽网格实现教程
本教程适合 HarmonyOS Next 初学者,通过简单到复杂的步骤,一步步实现类似桌面APP中的可拖拽编辑效果。
164 1
鸿蒙特效教程06-可拖拽网格实现教程
|
6月前
|
前端开发 算法 UED
鸿蒙特效教程04-直播点赞动画效果实现教程
本教程适合HarmonyOS初学者,通过简单到复杂的步骤,通过HarmonyOS的Canvas组件,一步步实现时下流行的点赞动画效果。
140 1
鸿蒙特效教程04-直播点赞动画效果实现教程
|
6月前
|
API UED 开发者
HarmonyOS:动画 motionPath 、 animateToImmediately API自学指南
在鸿蒙应用开发中,动画是提升用户体验的关键。本文针对初学者面对众多动画API时的困惑,重点解析两个实用API:`motionPath`和`animateToImmediately`。前者通过精细控制组件运动路径(如SVG字符串定义轨迹),实现灵动位移动画;后者从API Version 12起支持显式动画立即下发,结合状态变化打造流畅动画序列。文中提供详细参数说明与示例代码,帮助开发者快速掌握技巧,让应用更生动。
149 8
|
6月前
|
前端开发 JavaScript API
HarmonyOS:ArkTS 显式动画 animateTo 自学指南
本文深入解析了 ArkTS 中的 `animateTo` 全局显式动画接口,帮助开发者掌握其使用方法。文章从接口概述、参数详解到使用注意事项,结合实际示例代码,全面展示了如何通过配置 `AnimateParam` 对象实现流畅的动画效果。内容涵盖属性动画、布局变化及组件转场等场景,并强调不同版本的支持特性。适合初学者系统学习,也供进阶开发者参考优化动画体验。希望本文能助你快速上手 `animateTo`!
355 7

热门文章

最新文章