1e41f4b71Sopenharmony_ci# Application Animation Practice
2e41f4b71Sopenharmony_ci
3e41f4b71Sopenharmony_ci## Introduction
4e41f4b71Sopenharmony_ciAnimations add a lively, delightful touch to your application. Yet, they must be handled carefully to avoid performance issues. This topic describes how to make smart use of animations in applications, and examines how less frequent layout and property changes and avoidance of redundant re-renders can reduce performance overhead.
5e41f4b71Sopenharmony_ciSpecifically, the following modes are recommended in implementing animations:
6e41f4b71Sopenharmony_ci
7e41f4b71Sopenharmony_ci- Using **transition** for component transitions
8e41f4b71Sopenharmony_ci- Using transform properties for component layout changes
9e41f4b71Sopenharmony_ci- Using the same **animateTo** for properties with same animation settings.
10e41f4b71Sopenharmony_ci- Updating state variables for multiple **animateTo** calls at once
11e41f4b71Sopenharmony_ci
12e41f4b71Sopenharmony_ci## Smart Use of Animations
13e41f4b71Sopenharmony_ci
14e41f4b71Sopenharmony_ci### Using transition for Component Transitions
15e41f4b71Sopenharmony_ciTo apply an animation to a component when it appears or disappears, two common methods are available:
16e41f4b71Sopenharmony_ci
17e41f4b71Sopenharmony_ci- Use the **animateTo** API and add logic processing to the animation end callback.
18e41f4b71Sopenharmony_ci- Use the **transition** API.
19e41f4b71Sopenharmony_ci
20e41f4b71Sopenharmony_ci**transition** is preferred over **animateTo**, primarily for two reasons:
21e41f4b71Sopenharmony_ci
22e41f4b71Sopenharmony_ci- **transition** leads to higher performance, in that it involves only one property change with a conditional statement, while **animateTo** involves two property changes, one before the animation and one after.
23e41f4b71Sopenharmony_ci- **transition** is easier to implement, without the need for complex logic processing in the animation end callback.  
24e41f4b71Sopenharmony_ci
25e41f4b71Sopenharmony_ciAvoid: Change a component's opacity property from 1 to 0 to hide the component, and control the disappearance of the component in the animation end callback.
26e41f4b71Sopenharmony_ci
27e41f4b71Sopenharmony_ci```typescript
28e41f4b71Sopenharmony_ci@Entry
29e41f4b71Sopenharmony_ci@Component
30e41f4b71Sopenharmony_cistruct MyComponent {
31e41f4b71Sopenharmony_ci  @State mOpacity: number = 1;
32e41f4b71Sopenharmony_ci  @State show: boolean = true;
33e41f4b71Sopenharmony_ci  count: number = 0;
34e41f4b71Sopenharmony_ci  
35e41f4b71Sopenharmony_ci  build() {
36e41f4b71Sopenharmony_ci    Column() {
37e41f4b71Sopenharmony_ci      Row() {
38e41f4b71Sopenharmony_ci        if (this.show) {
39e41f4b71Sopenharmony_ci          Text('value')
40e41f4b71Sopenharmony_ci            .opacity(this.mOpacity)
41e41f4b71Sopenharmony_ci        }
42e41f4b71Sopenharmony_ci      }
43e41f4b71Sopenharmony_ci      .width('100%')
44e41f4b71Sopenharmony_ci      .height(100)
45e41f4b71Sopenharmony_ci      .justifyContent(FlexAlign.Center)
46e41f4b71Sopenharmony_ci      Text('toggle state')
47e41f4b71Sopenharmony_ci        .onClick(() => {
48e41f4b71Sopenharmony_ci          this.count++;
49e41f4b71Sopenharmony_ci          const thisCount: number = this.count;
50e41f4b71Sopenharmony_ci          this.show = true;
51e41f4b71Sopenharmony_ci          // Hide or hide the <Text> component by changing its opacity property.
52e41f4b71Sopenharmony_ci          animateTo({ duration: 1000, onFinish: () => {
53e41f4b71Sopenharmony_ci            // In the last animation, hide the <Text> component, and then change the conditional statement to make the component disappear.
54e41f4b71Sopenharmony_ci            if (thisCount === this.count && this.mOpacity === 0) {
55e41f4b71Sopenharmony_ci              this.show = false;
56e41f4b71Sopenharmony_ci            }
57e41f4b71Sopenharmony_ci          } }, () => {
58e41f4b71Sopenharmony_ci            this.mOpacity = this.mOpacity === 1 ? 0 : 1;
59e41f4b71Sopenharmony_ci          })
60e41f4b71Sopenharmony_ci        })
61e41f4b71Sopenharmony_ci    }
62e41f4b71Sopenharmony_ci  }
63e41f4b71Sopenharmony_ci}
64e41f4b71Sopenharmony_ci```
65e41f4b71Sopenharmony_ci
66e41f4b71Sopenharmony_ciPreferable: Directly use the **transition** API to animate the opacity when the **\<Text>** component appears or disappears.
67e41f4b71Sopenharmony_ci
68e41f4b71Sopenharmony_ci```typescript
69e41f4b71Sopenharmony_ci@Entry
70e41f4b71Sopenharmony_ci@Component
71e41f4b71Sopenharmony_cistruct MyComponent {
72e41f4b71Sopenharmony_ci  @State show: boolean = true;
73e41f4b71Sopenharmony_ci  
74e41f4b71Sopenharmony_ci  build() {
75e41f4b71Sopenharmony_ci    Column() {
76e41f4b71Sopenharmony_ci      Row() {
77e41f4b71Sopenharmony_ci        if (this.show) {
78e41f4b71Sopenharmony_ci          Text('value')
79e41f4b71Sopenharmony_ci            // Set the ID so that the transition can be interrupted.
80e41f4b71Sopenharmony_ci            .id('myText')
81e41f4b71Sopenharmony_ci            .transition(TransitionEffect.OPACITY.animation({ duration: 1000 }))
82e41f4b71Sopenharmony_ci        }
83e41f4b71Sopenharmony_ci      }.width('100%')
84e41f4b71Sopenharmony_ci      .height(100)
85e41f4b71Sopenharmony_ci      .justifyContent(FlexAlign.Center)
86e41f4b71Sopenharmony_ci      Text('toggle state')
87e41f4b71Sopenharmony_ci        .onClick(() => {
88e41f4b71Sopenharmony_ci          // Use transition to animate the opacity when the component appears or disappears.
89e41f4b71Sopenharmony_ci          this.show = !this.show;
90e41f4b71Sopenharmony_ci        })
91e41f4b71Sopenharmony_ci    }
92e41f4b71Sopenharmony_ci  }
93e41f4b71Sopenharmony_ci}
94e41f4b71Sopenharmony_ci```
95e41f4b71Sopenharmony_ci
96e41f4b71Sopenharmony_ci### Using Transform Properties for Component Layout Changes
97e41f4b71Sopenharmony_ciYou can change the layout of a component in either of the following methods:
98e41f4b71Sopenharmony_ci
99e41f4b71Sopenharmony_ci- Modify [layout properties](../ui/arkts-attribute-animation-overview.md), which will cause a UI re-layout. Common layout properties include **width**, **height**, and **layoutWeight**.
100e41f4b71Sopenharmony_ci- Modify [transform properties](../reference/arkui-ts/ts-universal-attributes-transformation.md), which will cause the component to translate, rotate, or scale.
101e41f4b71Sopenharmony_ci
102e41f4b71Sopenharmony_ciAs modifying transform properties does not involve the time-consuming UI re-layout, it is more time efficient than modifying layout properties, and is therefore recommended. The following examples use the preceding methods to scale up a component by 10 times.
103e41f4b71Sopenharmony_ci
104e41f4b71Sopenharmony_ciAvoid: Scale up a component by modifying its **width** and **height** properties.
105e41f4b71Sopenharmony_ci
106e41f4b71Sopenharmony_ci```typescript
107e41f4b71Sopenharmony_ci@Entry
108e41f4b71Sopenharmony_ci@Component
109e41f4b71Sopenharmony_cistruct MyComponent {
110e41f4b71Sopenharmony_ci  @State textWidth: number = 10;
111e41f4b71Sopenharmony_ci  @State textHeight: number = 10;
112e41f4b71Sopenharmony_ci
113e41f4b71Sopenharmony_ci  build() {
114e41f4b71Sopenharmony_ci    Column() {
115e41f4b71Sopenharmony_ci      Text()
116e41f4b71Sopenharmony_ci        .backgroundColor(Color.Blue)
117e41f4b71Sopenharmony_ci        .fontColor(Color.White)
118e41f4b71Sopenharmony_ci        .fontSize(20)
119e41f4b71Sopenharmony_ci        .width(this.textWidth)
120e41f4b71Sopenharmony_ci        .height(this.textHeight)
121e41f4b71Sopenharmony_ci
122e41f4b71Sopenharmony_ci      Button ('Layout Properties')
123e41f4b71Sopenharmony_ci        .backgroundColor(Color.Blue)
124e41f4b71Sopenharmony_ci        .fontColor(Color.White)
125e41f4b71Sopenharmony_ci        .fontSize(20)
126e41f4b71Sopenharmony_ci        .margin({ top: 30 })
127e41f4b71Sopenharmony_ci        .borderRadius(30)
128e41f4b71Sopenharmony_ci        .padding(10)
129e41f4b71Sopenharmony_ci        .onClick(() => {
130e41f4b71Sopenharmony_ci          animateTo({ duration: 1000 }, () => {
131e41f4b71Sopenharmony_ci            this.textWidth = 100;
132e41f4b71Sopenharmony_ci            this.textHeight = 100;
133e41f4b71Sopenharmony_ci          })
134e41f4b71Sopenharmony_ci        })
135e41f4b71Sopenharmony_ci    }
136e41f4b71Sopenharmony_ci}
137e41f4b71Sopenharmony_ci}
138e41f4b71Sopenharmony_ci```
139e41f4b71Sopenharmony_ci
140e41f4b71Sopenharmony_ciBecause animating the location and size properties of a component involves new UI measurement and layout, performance overhead is high. If a component's location or size changes continuously, as in a pinch gesture, using the **scale** property is a better choice for the animation, as in the example below.
141e41f4b71Sopenharmony_ci
142e41f4b71Sopenharmony_ciPreferable: Scale up a component by modifying the **scale** property.
143e41f4b71Sopenharmony_ci
144e41f4b71Sopenharmony_ci```typescript
145e41f4b71Sopenharmony_ci@Entry
146e41f4b71Sopenharmony_ci@Component
147e41f4b71Sopenharmony_cistruct MyComponent {
148e41f4b71Sopenharmony_ci  @State textScaleX: number = 1;
149e41f4b71Sopenharmony_ci  @State textScaleY: number = 1;
150e41f4b71Sopenharmony_ci
151e41f4b71Sopenharmony_ci  build() {
152e41f4b71Sopenharmony_ci    Column() {
153e41f4b71Sopenharmony_ci      Text()
154e41f4b71Sopenharmony_ci        .backgroundColor(Color.Blue)
155e41f4b71Sopenharmony_ci        .fontColor(Color.White)
156e41f4b71Sopenharmony_ci        .fontSize(20)
157e41f4b71Sopenharmony_ci        .width(10)
158e41f4b71Sopenharmony_ci        .height(10)
159e41f4b71Sopenharmony_ci        .scale({ x: this.textScaleX, y: this.textScaleY })
160e41f4b71Sopenharmony_ci        .margin({ top: 100 })
161e41f4b71Sopenharmony_ci
162e41f4b71Sopenharmony_ci      Button ('Transform Properties')
163e41f4b71Sopenharmony_ci        .backgroundColor(Color.Blue)
164e41f4b71Sopenharmony_ci        .fontColor(Color.White)
165e41f4b71Sopenharmony_ci        .fontSize(20)
166e41f4b71Sopenharmony_ci        .margin({ top: 60 })
167e41f4b71Sopenharmony_ci        .borderRadius(30)
168e41f4b71Sopenharmony_ci        .padding(10)
169e41f4b71Sopenharmony_ci        .onClick(() => {
170e41f4b71Sopenharmony_ci          animateTo({ duration: 1000 }, () => {
171e41f4b71Sopenharmony_ci            this.textScaleX = 10;
172e41f4b71Sopenharmony_ci            this.textScaleY = 10;
173e41f4b71Sopenharmony_ci          })
174e41f4b71Sopenharmony_ci        })
175e41f4b71Sopenharmony_ci    }
176e41f4b71Sopenharmony_ci}
177e41f4b71Sopenharmony_ci}
178e41f4b71Sopenharmony_ci```
179e41f4b71Sopenharmony_ci
180e41f4b71Sopenharmony_ci### Using Same animateTo for Properties with Same Animation Settings
181e41f4b71Sopenharmony_ciEach time **animateTo** is called, a before-after comparison is required. Less **animateTo** calls can contribute to fewer component re-renders and thereby higher performance.
182e41f4b71Sopenharmony_ciIn light of this, if properties share the same animation settings, consider placing them in the same animation closure.
183e41f4b71Sopenharmony_ci
184e41f4b71Sopenharmony_ciAvoid: Place state variables with the same animation settings in different animation closures.
185e41f4b71Sopenharmony_ci
186e41f4b71Sopenharmony_ci```typescript
187e41f4b71Sopenharmony_ci@Entry
188e41f4b71Sopenharmony_ci@Component
189e41f4b71Sopenharmony_cistruct MyComponent {
190e41f4b71Sopenharmony_ci  @State textWidth: number = 200;
191e41f4b71Sopenharmony_ci  @State color: Color = Color.Red;
192e41f4b71Sopenharmony_ci  
193e41f4b71Sopenharmony_ci  func1() {
194e41f4b71Sopenharmony_ci    animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {
195e41f4b71Sopenharmony_ci      this.textWidth = (this.textWidth === 100 ? 200 : 100);
196e41f4b71Sopenharmony_ci    });
197e41f4b71Sopenharmony_ci  }
198e41f4b71Sopenharmony_ci  
199e41f4b71Sopenharmony_ci  func2() {
200e41f4b71Sopenharmony_ci    animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {
201e41f4b71Sopenharmony_ci      this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);
202e41f4b71Sopenharmony_ci    });
203e41f4b71Sopenharmony_ci  }
204e41f4b71Sopenharmony_ci  
205e41f4b71Sopenharmony_ci  build() {
206e41f4b71Sopenharmony_ci    Column() {
207e41f4b71Sopenharmony_ci      Row()
208e41f4b71Sopenharmony_ci        .width(this.textWidth)
209e41f4b71Sopenharmony_ci        .height(10)
210e41f4b71Sopenharmony_ci        .backgroundColor(this.color)
211e41f4b71Sopenharmony_ci      Text('click')
212e41f4b71Sopenharmony_ci        .onClick(() => {
213e41f4b71Sopenharmony_ci          this.func1();
214e41f4b71Sopenharmony_ci          this.func2();
215e41f4b71Sopenharmony_ci        })
216e41f4b71Sopenharmony_ci    }
217e41f4b71Sopenharmony_ci    .width('100%')
218e41f4b71Sopenharmony_ci    .height('100%')
219e41f4b71Sopenharmony_ci  }
220e41f4b71Sopenharmony_ci}
221e41f4b71Sopenharmony_ci```
222e41f4b71Sopenharmony_ci
223e41f4b71Sopenharmony_ciPreferable: Combine animations with the same animation settings into one animation closure.
224e41f4b71Sopenharmony_ci
225e41f4b71Sopenharmony_ci```typescript
226e41f4b71Sopenharmony_ci@Entry
227e41f4b71Sopenharmony_ci@Component
228e41f4b71Sopenharmony_cistruct MyComponent {
229e41f4b71Sopenharmony_ci  @State textWidth: number = 200;
230e41f4b71Sopenharmony_ci  @State color: Color = Color.Red;
231e41f4b71Sopenharmony_ci  
232e41f4b71Sopenharmony_ci  func() {
233e41f4b71Sopenharmony_ci    animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {
234e41f4b71Sopenharmony_ci      this.textWidth = (this.textWidth === 100 ? 200 : 100);
235e41f4b71Sopenharmony_ci      this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);
236e41f4b71Sopenharmony_ci    });
237e41f4b71Sopenharmony_ci  }
238e41f4b71Sopenharmony_ci  
239e41f4b71Sopenharmony_ci  build() {
240e41f4b71Sopenharmony_ci    Column() {
241e41f4b71Sopenharmony_ci      Row()
242e41f4b71Sopenharmony_ci        .width(this.textWidth)
243e41f4b71Sopenharmony_ci        .height(10)
244e41f4b71Sopenharmony_ci        .backgroundColor(this.color)
245e41f4b71Sopenharmony_ci      Text('click')
246e41f4b71Sopenharmony_ci        .onClick(() => {
247e41f4b71Sopenharmony_ci          this.func();
248e41f4b71Sopenharmony_ci        })
249e41f4b71Sopenharmony_ci    }
250e41f4b71Sopenharmony_ci    .width('100%')
251e41f4b71Sopenharmony_ci    .height('100%')
252e41f4b71Sopenharmony_ci  }
253e41f4b71Sopenharmony_ci}
254e41f4b71Sopenharmony_ci```
255e41f4b71Sopenharmony_ci
256e41f4b71Sopenharmony_ci### Updating State Variables for Multiple animateTo Calls At Once
257e41f4b71Sopenharmony_ci**animateTo** compares the states before and after the animation closure is executed and animates the differences. For comparison, all changed state variables and dirty nodes are re-rendered before the animation closure of **animateTo** is executed.
258e41f4b71Sopenharmony_ciIf state variables are re-rendered between **animateTo** calls, there may exist dirty nodes that need to be re-rendered before the next **animateTo** call, which may cause redundant re-renders.
259e41f4b71Sopenharmony_ci
260e41f4b71Sopenharmony_ciAvoid: Re-render state variables between **animateTo** calls.
261e41f4b71Sopenharmony_ci
262e41f4b71Sopenharmony_ci![Re-render State Variables Between animateTo Calls](figures/multi_animateto.png)
263e41f4b71Sopenharmony_ci
264e41f4b71Sopenharmony_ciThe following code updates other states of a component between two **animateTo** calls.
265e41f4b71Sopenharmony_ci
266e41f4b71Sopenharmony_ci```typescript
267e41f4b71Sopenharmony_ci@Entry
268e41f4b71Sopenharmony_ci@Component
269e41f4b71Sopenharmony_cistruct MyComponent {
270e41f4b71Sopenharmony_ci  @State textWidth: number = 200;
271e41f4b71Sopenharmony_ci  @State textHeight: number = 50;
272e41f4b71Sopenharmony_ci  @State color: Color = Color.Red;
273e41f4b71Sopenharmony_ci  
274e41f4b71Sopenharmony_ci  build() {
275e41f4b71Sopenharmony_ci    Column() {
276e41f4b71Sopenharmony_ci      Row()
277e41f4b71Sopenharmony_ci        .width(this.textWidth)
278e41f4b71Sopenharmony_ci        .height(10)
279e41f4b71Sopenharmony_ci        .backgroundColor(this.color)
280e41f4b71Sopenharmony_ci      Text('click')
281e41f4b71Sopenharmony_ci        .height(this.textHeight)
282e41f4b71Sopenharmony_ci        .onClick(() => {
283e41f4b71Sopenharmony_ci          this.textWidth = 100;
284e41f4b71Sopenharmony_ci          // textHeight is a non-animatable property.
285e41f4b71Sopenharmony_ci          this.textHeight = 100;
286e41f4b71Sopenharmony_ci          animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {
287e41f4b71Sopenharmony_ci            this.textWidth = 200;
288e41f4b71Sopenharmony_ci          });
289e41f4b71Sopenharmony_ci          this.color = Color.Yellow;
290e41f4b71Sopenharmony_ci          animateTo({ curve: Curve.Linear, duration: 2000 }, () => {
291e41f4b71Sopenharmony_ci            this.color = Color.Red;
292e41f4b71Sopenharmony_ci          });
293e41f4b71Sopenharmony_ci        })
294e41f4b71Sopenharmony_ci    }
295e41f4b71Sopenharmony_ci    .width('100%')
296e41f4b71Sopenharmony_ci    .height('100%')
297e41f4b71Sopenharmony_ci  }
298e41f4b71Sopenharmony_ci}
299e41f4b71Sopenharmony_ci```
300e41f4b71Sopenharmony_ci
301e41f4b71Sopenharmony_ciBefore the first **animateTo** call, the **textWidth** property is modified. Therefore, the **\<Row>** component needs to be re-rendered. In the animation closure of the first **animateTo**, the **textWidth** property is modified. Therefore, the **\<Row>** component needs to be re-rendered again and compared with the last rendering result to generate a width and height animation. Before the second **animateTo** call, the **color** property is modified. Therefore, the **\<Row>** component needs to be re-rendered again. In the animation closure of the second **animateTo** call, the **color** property is modified. Therefore, the **\<Row>** component needs to be re-rendered again to generate a background color animation. In sum, the **\<Row>** component is re-rendered four times for its property changes.
302e41f4b71Sopenharmony_ciIn this example, the state variable **textHeight** irrelevant to the animation is also modified. If the state change is not needed, it should be avoided to reduce redundant re-renders.
303e41f4b71Sopenharmony_ci
304e41f4b71Sopenharmony_ciPreferable: Update state variables in a unified manner.
305e41f4b71Sopenharmony_ci
306e41f4b71Sopenharmony_ci![Update State Variables in a Unified Manner 1](figures/unify_animateto.png) or ![Update State Variables in a Unified Manner 2](figures/unify_animateto_three_step.png)
307e41f4b71Sopenharmony_ci
308e41f4b71Sopenharmony_ciPreferable 1: Use the original state before **animateTo** to drive the animation from the original state to the target state. In this way, abrupt, unnatural changes can be avoided at the beginning of the animation.
309e41f4b71Sopenharmony_ci
310e41f4b71Sopenharmony_ci```typescript
311e41f4b71Sopenharmony_ci@Entry
312e41f4b71Sopenharmony_ci@Component
313e41f4b71Sopenharmony_cistruct MyComponent {
314e41f4b71Sopenharmony_ci  @State textWidth: number = 100;
315e41f4b71Sopenharmony_ci  @State textHeight: number = 50;
316e41f4b71Sopenharmony_ci  @State color: Color = Color.Yellow;
317e41f4b71Sopenharmony_ci  
318e41f4b71Sopenharmony_ci  build() {
319e41f4b71Sopenharmony_ci    Column() {
320e41f4b71Sopenharmony_ci      Row()
321e41f4b71Sopenharmony_ci        .width(this.textWidth)
322e41f4b71Sopenharmony_ci        .height(10)
323e41f4b71Sopenharmony_ci        .backgroundColor(this.color)
324e41f4b71Sopenharmony_ci      Text('click')
325e41f4b71Sopenharmony_ci        .height(this.textHeight)
326e41f4b71Sopenharmony_ci        .onClick(() => {
327e41f4b71Sopenharmony_ci          animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {
328e41f4b71Sopenharmony_ci            this.textWidth = (this.textWidth === 100 ? 200 : 100);
329e41f4b71Sopenharmony_ci          });
330e41f4b71Sopenharmony_ci          animateTo({ curve: Curve.Linear, duration: 2000 }, () => {
331e41f4b71Sopenharmony_ci            this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);
332e41f4b71Sopenharmony_ci          });
333e41f4b71Sopenharmony_ci        })
334e41f4b71Sopenharmony_ci    }
335e41f4b71Sopenharmony_ci    .width('100%')
336e41f4b71Sopenharmony_ci    .height('100%')
337e41f4b71Sopenharmony_ci  }
338e41f4b71Sopenharmony_ci}
339e41f4b71Sopenharmony_ci```
340e41f4b71Sopenharmony_ci
341e41f4b71Sopenharmony_ciBefore the first **animateTo** call, no dirty state variable or dirty node needs to be updated, and no re-render is required. In the animation closure of the first **animateTo**, the **textWidth** property is modified. Therefore, the **\<Row>** component needs to be re-rendered and compared with the last rendering result to generate a width and height animation. Before the second **animateTo** call, because no additional statement is executed, there is no dirty state variable or dirty node that needs to be updated, and no re-render is required. In the animation closure of the second **animateTo** call, the **color** property is modified. Therefore, the **\<Row>** component needs to be re-rendered again to generate a background color animation. In sum, the **\<Row>** component is re-rendered twice for its property changes.
342e41f4b71Sopenharmony_ci
343e41f4b71Sopenharmony_ciPreferable 2: Explicitly specify the initial values of all properties that require animation before **animateTo**, update the values to the node, and then animate the properties.
344e41f4b71Sopenharmony_ci
345e41f4b71Sopenharmony_ci```typescript
346e41f4b71Sopenharmony_ci@Entry
347e41f4b71Sopenharmony_ci@Component
348e41f4b71Sopenharmony_cistruct MyComponent {
349e41f4b71Sopenharmony_ci  @State textWidth: number = 200;
350e41f4b71Sopenharmony_ci  @State textHeight: number = 50;
351e41f4b71Sopenharmony_ci  @State color: Color = Color.Red;
352e41f4b71Sopenharmony_ci  
353e41f4b71Sopenharmony_ci  build() {
354e41f4b71Sopenharmony_ci    Column() {
355e41f4b71Sopenharmony_ci      Row()
356e41f4b71Sopenharmony_ci        .width(this.textWidth)
357e41f4b71Sopenharmony_ci        .height(10)
358e41f4b71Sopenharmony_ci        .backgroundColor(this.color)
359e41f4b71Sopenharmony_ci      Text('click')
360e41f4b71Sopenharmony_ci        .height(this.textHeight)
361e41f4b71Sopenharmony_ci        .onClick(() => {
362e41f4b71Sopenharmony_ci          this.textWidth = 100;
363e41f4b71Sopenharmony_ci          this.color = Color.Yellow;
364e41f4b71Sopenharmony_ci          animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {
365e41f4b71Sopenharmony_ci            this.textWidth = 200;
366e41f4b71Sopenharmony_ci          });
367e41f4b71Sopenharmony_ci          animateTo({ curve: Curve.Linear, duration: 2000 }, () => {
368e41f4b71Sopenharmony_ci            this.color = Color.Red;
369e41f4b71Sopenharmony_ci          });
370e41f4b71Sopenharmony_ci          this.textHeight = 100;
371e41f4b71Sopenharmony_ci        })
372e41f4b71Sopenharmony_ci    }
373e41f4b71Sopenharmony_ci    .width('100%')
374e41f4b71Sopenharmony_ci    .height('100%')
375e41f4b71Sopenharmony_ci  }
376e41f4b71Sopenharmony_ci}
377e41f4b71Sopenharmony_ci```
378e41f4b71Sopenharmony_ci
379e41f4b71Sopenharmony_ciBefore the first **animateTo** call, the **textWidth** and **color** properties are modified. Therefore, the **\<Row>** component needs to be re-rendered. In the animation closure of the first **animateTo**, the **textWidth** property is modified. Therefore, the **\<Row>** component needs to be re-rendered again and compared with the last rendering result to generate a width and height animation. Before the second **animateTo** call, because no additional statement is executed, there is no dirty state variable or dirty node that needs to be updated, and no re-render is required. In the animation closure of the second **animateTo** call, the **color** property is modified. Therefore, the **\<Row>** component needs to be re-rendered again to generate a background color animation. In sum, the **\<Row>** component is re-rendered three times for its property changes.
380