1# 实现属性动画
2
3
4通过可动画属性改变引起UI上产生的连续视觉效果,即为属性动画。属性动画是最基础易懂的动画,ArkUI提供两种属性动画接口[animateTo](../reference/apis-arkui/arkui-ts/ts-explicit-animation.md)和[animation](../reference/apis-arkui/arkui-ts/ts-animatorproperty.md)驱动组件属性按照动画曲线等动画参数进行连续的变化,产生属性动画。
5
6
7| 属性动画接口 | 作用域 | 原理 | 使用场景 |
8| -------- | -------- | -------- | -------- |
9| animateTo | 闭包内改变属性引起的界面变化。<br/>作用于出现消失转场。 | 通用函数,对闭包前界面和闭包中的状态变量引起的界面之间的差异做动画。<br/>支持多次调用,支持嵌套。 | 适用对多个可动画属性配置相同动画参数的动画。<br/>需要嵌套使用动画的场景。 |
10| animation | 组件通过属性接口绑定的属性变化引起的界面变化。 | 识别组件的可动画属性变化,自动添加动画。<br/>组件的接口调用是从下往上执行,animation只会作用于在其之上的属性调用。<br/>组件可以根据调用顺序对多个属性设置不同的animation。 | 适用于对多个可动画属性配置不同参数动画的场景。 |
11
12## 使用animateTo产生属性动画
13
14
15```
16animateTo(value: AnimateParam, event: () => void): void
17```
18
19[animateTo](../reference/apis-arkui/arkui-ts/ts-explicit-animation.md)接口参数中,value指定[AnimateParam对象](../reference/apis-arkui/arkui-ts/ts-explicit-animation.md#animateparam对象说明)(包括时长、[Curve](../reference/apis-arkui/js-apis-curve.md#curve)等)event为动画的闭包函数,闭包内变量改变产生的属性动画将遵循相同的动画参数。
20
21> **说明:**
22>
23> 直接使用animateTo可能导致实例不明确的问题,建议使用[getUIContext](../reference/apis-arkui/js-apis-window.md#getuicontext10)获取[UIContext](../reference/apis-arkui/js-apis-arkui-UIContext.md#uicontext)实例,并使用[animateTo](../reference/apis-arkui/js-apis-arkui-UIContext.md#animateto)调用绑定实例的animateTo。
24
25```ts
26import { curves } from '@kit.ArkUI';
27
28@Entry
29@Component
30struct AnimateToDemo {
31  @State animate: boolean = false;
32  // 第一步: 声明相关状态变量
33  @State rotateValue: number = 0; // 组件一旋转角度
34  @State translateX: number = 0; // 组件二偏移量
35  @State opacityValue: number = 1; // 组件二透明度
36
37  // 第二步:将状态变量设置到相关可动画属性接口
38  build() {
39    Row() {
40      // 组件一
41      Column() {
42      }
43      .rotate({ angle: this.rotateValue })
44      .backgroundColor('#317AF7')
45      .justifyContent(FlexAlign.Center)
46      .width(100)
47      .height(100)
48      .borderRadius(30)
49      .onClick(() => {
50        this.getUIContext()?.animateTo({ curve: curves.springMotion() }, () => {
51          this.animate = !this.animate;
52          // 第三步:闭包内通过状态变量改变UI界面
53          // 这里可以写任何能改变UI的逻辑比如数组添加,显隐控制,系统会检测改变后的UI界面与之前的UI界面的差异,对有差异的部分添加动画
54          // 组件一的rotate属性发生变化,所以会给组件一添加rotate旋转动画
55          this.rotateValue = this.animate ? 90 : 0;
56          // 组件二的透明度发生变化,所以会给组件二添加透明度的动画
57          this.opacityValue = this.animate ? 0.6 : 1;
58          // 组件二的translate属性发生变化,所以会给组件二添加translate偏移动画
59          this.translateX = this.animate ? 50 : 0;
60        })
61      })
62
63      // 组件二
64      Column() {
65
66      }
67      .justifyContent(FlexAlign.Center)
68      .width(100)
69      .height(100)
70      .backgroundColor('#D94838')
71      .borderRadius(30)
72      .opacity(this.opacityValue)
73      .translate({ x: this.translateX })
74    }
75    .width('100%')
76    .height('100%')
77    .justifyContent(FlexAlign.Center)
78  }
79}
80```
81
82![zh-cn_image_0000001599958466](figures/zh-cn_image_0000001599958466.gif)
83
84
85## 使用animation产生属性动画
86
87相比于animateTo接口需要把要执行动画的属性的修改放在闭包中,[animation](../reference/apis-arkui/arkui-ts/ts-animatorproperty.md)接口无需使用闭包,把animation接口加在要做属性动画的可动画属性后即可。animation只要检测到其绑定的可动画属性发生变化,就会自动添加属性动画,animateTo则必须在动画闭包内改变可动画属性的值从而生成动画。
88
89
90```ts
91import { curves } from '@kit.ArkUI';
92
93@Entry
94@Component
95struct AnimationDemo {
96  @State animate: boolean = false;
97  // 第一步: 声明相关状态变量
98  @State rotateValue: number = 0; // 组件一旋转角度
99  @State translateX: number = 0; // 组件二偏移量
100  @State opacityValue: number = 1; // 组件二透明度
101
102  // 第二步:将状态变量设置到相关可动画属性接口
103  build() {
104    Row() {
105      // 组件一
106      Column() {
107      }
108      .opacity(this.opacityValue)
109      .rotate({ angle: this.rotateValue })
110      // 第三步:通过属性动画接口开启属性动画
111      .animation({ curve: curves.springMotion() })
112      .backgroundColor('#317AF7')
113      .justifyContent(FlexAlign.Center)
114      .width(100)
115      .height(100)
116      .borderRadius(30)
117      .onClick(() => {
118        this.animate = !this.animate;
119        // 第四步:闭包内通过状态变量改变UI界面
120        // 这里可以写任何能改变UI的逻辑比如数组添加,显隐控制,系统会检测改变后的UI界面与之前的UI界面的差异,对有差异的部分添加动画
121        // 组件一的rotate属性发生变化,所以会给组件一添加rotate旋转动画
122        this.rotateValue = this.animate ? 90 : 0;
123        // 组件二的translate属性发生变化,所以会给组件二添加translate偏移动画
124        this.translateX = this.animate ? 50 : 0;
125        // 父组件column的opacity属性有变化,会导致其子节点的透明度也变化,所以这里会给column和其子节点的透明度属性都加动画
126        this.opacityValue = this.animate ? 0.6 : 1;
127      })
128
129      // 组件二
130      Column() {
131      }
132      .justifyContent(FlexAlign.Center)
133      .width(100)
134      .height(100)
135      .backgroundColor('#D94838')
136      .borderRadius(30)
137      .opacity(this.opacityValue)
138      .translate({ x: this.translateX })
139      .animation({ curve: curves.springMotion() })
140    }
141    .width('100%')
142    .height('100%')
143    .justifyContent(FlexAlign.Center)
144  }
145}
146```
147
148![zh-cn_image_0000001649279705](figures/zh-cn_image_0000001649279705.gif)
149
150> **说明:**
151> - 在对组件的位置大小的变化做动画的时候,由于布局属性的改变会触发测量布局,性能开销大。[scale](../reference/apis-arkui/arkui-ts/ts-universal-attributes-transformation.md#scale)属性的改变不会触发测量布局,性能开销小。因此,在组件位置大小持续发生变化的场景,如跟手触发组件大小变化的场景,推荐适用scale。
152>
153> - 属性动画应该作用于始终存在的组件,对于将要出现或者将要消失的组件的动画应该使用[转场动画](arkts-transition-overview.md)。
154>
155> - 尽量不要使用动画结束回调。属性动画是对已经发生的状态进行的动画,不需要开发者去处理结束的逻辑。如果要使用结束回调,一定要正确处理连续操作的数据管理。
156