
本教程将带领大家从零开始,一步步讲解如何讲解 animateTo 动画,并实现按钮交互效果,使新手也能轻松掌握。
下载代码仓库
通过两个常见的按钮动画效果,深入学习 HarmonyOS Next 的 animateTo 动画,以及探索最佳实践。

HarmonyOS的ArkUI框架提供了强大的动画支持,常见有两种实现方式:
.animation()属性直接应用于组件animateTo()方法动态改变状态触发动画本文将主要使用animateTo()方法,因为它更灵活,能实现更复杂的动画效果。
首先,我们创建一个基本的页面组件结构:
@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()
  }
}这段代码创建了一个基本的页面布局,包含一个标题文本。接下来,我们将逐步添加按钮和动画效果。
首先,添加一个按钮及其容器:
// 按钮缩放效果
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)这段代码添加了一个带标题的按钮区域,并为按钮设置了基本样式。
要实现缩放效果,我们需要添加一个状态变量来控制按钮的缩放比例:
@State buttonScale: number = 1.0然后,为按钮添加缩放属性:
Button('点击缩放')
  .width(150)
  .fontSize(16)
  .scale({ x: this.buttonScale, y: this.buttonScale })  // 添加缩放属性
  .onClick(() => {
    console.log('按钮被点击了')
  }).scale()属性用于设置组件的缩放比例,通过改变buttonScale的值,可以实现按钮的缩放效果。
现在,添加一个简单的点击缩放效果:
// 按钮点击缩放效果
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,我们可以进一步优化。
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:急缓曲线,开始快速然后迅速减慢,产生弹性的视觉效果先添加抖动按钮的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)要实现抖动效果,我们需要添加状态变量来控制按钮的水平位移:
@State shakeOffset: number = 0  // 控制水平抖动偏移
@State shakeStep: number = 0    // 用于跟踪抖动步骤然后,为按钮添加平移属性:
Button('点击抖动')
  .width(150)
  .fontSize(16)
  .translate({ x: this.shakeOffset })  // 添加水平平移属性
  .onClick(() => {
    console.log('按钮被点击了')
  }).translate()属性用于设置组件的平移,通过改变shakeOffset的值,我们可以让按钮左右移动。
一个简单的实现方式是使用多个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不够优雅,我们可以进一步优化。
我们可以使用递归和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]  // 设置当前步骤的偏移值
  })
}这种实现方式更加优雅和灵活:
shakeValues定义整个抖动序列executeShakeStep()和onFinish回调,实现连续动画setTimeout,使代码更加清晰和易于维护animateTo()是HarmonyOS中实现动画的核心API,它的基本语法如下:
animateTo(value: AnimateParam, event: () => void): voidAnimateParam是一个配置对象,包含以下主要属性:
HarmonyOS提供了多种缓动曲线,可以实现不同的动画效果:
event是一个函数,在这个函数中改变状态变量的值,从而触发动画。例如:
animateTo({ duration: 300 }, () => {
  this.buttonScale = 0.9  // 改变状态变量,触发缩放动画
})有几种方式可以实现连续的动画效果:
animateTo({ duration: 300 }, () => { this.value1 = newValue1 })
setTimeout(() => {
    animateTo({ duration: 300 }, () => { this.value2 = newValue2 })
}, 300)animateTo({
    duration: 300,
    onFinish: () => {
    animateTo({ duration: 300 }, () => { this.value2 = newValue2 })
    }
}, () => {
    this.value1 = newValue1
})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()
  }
}onFinish回调代替setTimeout实现连续动画通过本文,我们学习了如何在HarmonyOS中实现按钮缩放和抖动效果,关键点包括:
@State状态变量控制动画参数animateTo()方法实现流畅的状态变化动画onFinish回调和递归实现连续动画,避免使用setTimeout动画效果能够显著提升应用的用户体验,希望本文能帮助你在HarmonyOS应用中添加生动、自然的交互动画。随着你对 animateTo() API的深入理解,可以创造出更加复杂和精美的动画效果。
希望这篇 HarmonyOS Next 教程对你有所帮助,期待您的点赞、评论、收藏。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。