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 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 or  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