在适配鸿蒙应用的动画模块时,我们遇到了一个典型需求:实现类似Flutter中RotationTransition的旋转动画,用于加载等待、按钮反馈或页面切换。本文基于实际适配经验,介绍如何在ArkTS中利用rotate属性和animateTo实现等效效果,并对比Flutter核心差异,帮助开发者避免常见陷阱。
## 鸿蒙ArkTS旋转动画基础
鸿蒙ArkTS的旋转动画依赖组件自身的`rotate`属性结合`animateTo`或`animation`方法实现。与Flutter的`RotationTransition`不同,ArkTS采用角度制(单位:度),而非圈数制。
最简单的示例:让一个图标从0度旋转到360度(即一整圈):
- @State angle: number = 0
- build() {
- Image($r('app.media.icon'))
- .width(40)
- .height(40)
- .rotate({ angle: this.angle })
- .animation({ duration: 1200, curve: Curve.Linear })
- }
- // 在适当位置触发动画
- this.animateTo({ duration: 1200 }, () => {
- this.angle = 360
- })
复制代码
这里使用`Image`组件展示自定义图标,`.rotate()`接受一个对象,其中`angle`为当前旋转角度。通过`.animation()`设置持续时间和曲线,调用`animateTo`即可驱动状态变化。
## 无限旋转实现自定义加载图标
Flutter中通过`AnimationController.repeat()`实现无限旋转,鸿蒙ArkTS则可用`setInterval`或`Task`周期重置角度。以下为简洁实现:
- @State angle: number = 0
- private taskId: number = -1
- aboutToAppear() {
- this.taskId = setInterval(() => {
- this.angle = (this.angle + 10) % 360
- }, 30) // 每30ms增加10度,约1200ms转一圈
- }
- aboutToDisappear() {
- clearInterval(this.taskId)
- }
复制代码
但这种方法动画帧不连续。更优雅的方式是使用`animateTo`配合循环,如:
- async startSpin() {
- while (true) {
- await this.animateTo({ duration: 1200 }, () => { this.angle = 360 })
- this.angle = 0
- }
- }
复制代码
注意:无限循环需谨慎使用,避免内存泄漏。建议在离开页面时取消。
## 关键差异与常见陷阱
### 1. 角度制 vs 圈数制
Flutter的`turns`参数以圈数为单位(1.0=360°),而ArkTS直接使用角度。这对开发者更直观,无需手动换算。例如旋转90度直接写`angle: 90`,而非`0.25`。
### 2. 旋转轴心
ArkTS的`rotate`默认绕组件中心旋转,但可通过`rotate`的`centerX`、`centerY`属性调整轴心,如`rotate({ angle: 90, centerX: '0%', centerY: '0%' })`绕左上角旋转。效果类似Flutter的`alignment: Alignment.topLeft`。
### 3. 对称图标不显效
与Flutter同样,若旋转对象为对称图形(如纯圆形),旋转不可见。建议使用有方向性的图标(箭头、指针)或添加小标记。我们曾因使用系统圆形加载图标,调试半天才发现问题。
### 4. 曲线与循环的配合
鸿蒙支持多种曲线(`Curve.EaseInOut`、`Curve.BounceOut`等)。若需无限循环旋转,建议使用线性曲线,否则终点处的“弹跳”会导致视觉卡顿。这与Flutter中`repeat`配合不对称曲线的问题一致。
## 复杂组合动画:旋转+缩放
鸿蒙ArkTS要实现旋转同时缩放,可直接用`opacity`、`scale`等属性组合:
- @State scale: number = 1
- @State angle: number = 0
- build() {
- Column() {
- Image($r('app.media.icon'))
- .scale({ x: this.scale, y: this.scale })
- .rotate({ angle: this.angle })
- .animation({ duration: 800, curve: Curve.EaseOut })
- .onClick(() => {
- this.animateTo({ duration: 600 }, () => {
- this.scale = 1.5
- this.angle = 180
- })
- })
- }
- }
复制代码
这种写法比Flutter中嵌套Transition组件更简洁,但灵活性略逊——Flutter可通过一个`AnimationController`同时驱动多个Transition,实现更复杂的编排。鸿蒙ArkTS若需多阶段序列动画,需要使用`animateTo`链式调用或自定义Animation控制器。
## 总结
鸿蒙ArkTS的`rotate`属性结合`animation`和`animateTo`基本能覆盖Flutter中RotationTransition的常见场景,角度制对开发更友好。但若需要精细的显式动画编排(如多段不同时长的旋转序列),仍推荐使用ArkTS的`Animator`类或基于状态机的复杂控制。核心经验:方向性图标、轴心设置、曲线选择是旋转动画的三大易错点。验证环境:HarmonyOS 6.0 · DevEco Studio 4.0 · 真机nova12 Ultra。 |