1e41f4b71Sopenharmony_ci# Custom Property Animation
2e41f4b71Sopenharmony_ci
3e41f4b71Sopenharmony_ci
4e41f4b71Sopenharmony_ciThe property animation is an illusion of movement created on the UI when the value of an animatable property changes over time. It is implemented by setting continuous value changes of a property to the property API that can cause the UI re-render.
5e41f4b71Sopenharmony_ci
6e41f4b71Sopenharmony_ci
7e41f4b71Sopenharmony_ciArkUI provides the [@AnimatableExtend](../quick-start/arkts-animatable-extend.md) decorator for customizing animatable property APIs. Since the data type of the parameters must have a certain degree of continuity, the parameter types for custom animatable property APIs only support the number type and custom types that implement the [AnimtableArithmetic\<T>](../quick-start/arkts-animatable-extend.md) API. With custom animatable property APIs and animatable data types, you can achieve animation effects on non-animatable property APIs by modifying their values through a per-frame callback function when using **animateTo** or **animation**. Additionally, you can implement frame-by-frame layout effects by modifying the values of animatable properties in the per-frame callback function.
8e41f4b71Sopenharmony_ci
9e41f4b71Sopenharmony_ci
10e41f4b71Sopenharmony_ci## Implementing Frame-by-Frame Layout Effects for Text Component Width Using the number Data Type and @AnimatableExtend Decorator
11e41f4b71Sopenharmony_ci
12e41f4b71Sopenharmony_ci
13e41f4b71Sopenharmony_ci```ts
14e41f4b71Sopenharmony_ci// Step 1: Use the @AnimatableExtend decorator to customize an animatable property API.
15e41f4b71Sopenharmony_ci@AnimatableExtend(Text)
16e41f4b71Sopenharmony_cifunction animatableWidth(width: number) {
17e41f4b71Sopenharmony_ci  .width(width)// Call the system property API. The per-frame callback function modifies the value of the animatable property on each frame, achieving the effect of frame-by-frame layout.
18e41f4b71Sopenharmony_ci}
19e41f4b71Sopenharmony_ci
20e41f4b71Sopenharmony_ci@Entry
21e41f4b71Sopenharmony_ci@Component
22e41f4b71Sopenharmony_cistruct AnimatablePropertyExample {
23e41f4b71Sopenharmony_ci  @State textWidth: number = 80;
24e41f4b71Sopenharmony_ci
25e41f4b71Sopenharmony_ci  build() {
26e41f4b71Sopenharmony_ci    Column() {
27e41f4b71Sopenharmony_ci      Text("AnimatableProperty")
28e41f4b71Sopenharmony_ci        .animatableWidth(this.textWidth)// Step 2: Set the custom animatable property API on the component.
29e41f4b71Sopenharmony_ci        .animation({ duration: 2000, curve: Curve.Ease })// Step 3: Bind an animation to the custom animatable property API.
30e41f4b71Sopenharmony_ci      Button("Play")
31e41f4b71Sopenharmony_ci        .onClick(() => {
32e41f4b71Sopenharmony_ci          this.textWidth = this.textWidth == 80 ? 160 : 80;// Change the parameters of the custom animatable property to create an animation.
33e41f4b71Sopenharmony_ci        })
34e41f4b71Sopenharmony_ci    }.width("100%")
35e41f4b71Sopenharmony_ci    .padding(10)
36e41f4b71Sopenharmony_ci  }
37e41f4b71Sopenharmony_ci}
38e41f4b71Sopenharmony_ci```
39e41f4b71Sopenharmony_ci
40e41f4b71Sopenharmony_ci
41e41f4b71Sopenharmony_ci
42e41f4b71Sopenharmony_ci![en-us_image_0000001600119626](figures/en-us_image_0000001600119626.gif)
43e41f4b71Sopenharmony_ci
44e41f4b71Sopenharmony_ci
45e41f4b71Sopenharmony_ci## Changing Graphic Shapes Using Custom Data Types and \@AnimatableExtend Decorator
46e41f4b71Sopenharmony_ci
47e41f4b71Sopenharmony_ci
48e41f4b71Sopenharmony_ci```ts
49e41f4b71Sopenharmony_cideclare type Point = number[];
50e41f4b71Sopenharmony_ci
51e41f4b71Sopenharmony_ci// Define the parameter type of the animatable property API and implement the addition, subtraction, multiplication, and equivalence judgment functions in the AnimtableArithmetic<T> API.
52e41f4b71Sopenharmony_ciclass PointClass extends Array<number> {
53e41f4b71Sopenharmony_ci  constructor(value: Point) {
54e41f4b71Sopenharmony_ci    super(value[0], value[1])
55e41f4b71Sopenharmony_ci  }
56e41f4b71Sopenharmony_ci
57e41f4b71Sopenharmony_ci  add(rhs: PointClass): PointClass {
58e41f4b71Sopenharmony_ci    let result: Point = new Array<number>() as Point;
59e41f4b71Sopenharmony_ci    for (let i = 0; i < 2; i++) {
60e41f4b71Sopenharmony_ci      result.push(rhs[i] + this[i])
61e41f4b71Sopenharmony_ci    }
62e41f4b71Sopenharmony_ci    return new PointClass(result);
63e41f4b71Sopenharmony_ci  }
64e41f4b71Sopenharmony_ci
65e41f4b71Sopenharmony_ci  subtract(rhs: PointClass): PointClass {
66e41f4b71Sopenharmony_ci    let result: Point = new Array<number>() as Point;
67e41f4b71Sopenharmony_ci    for (let i = 0; i < 2; i++) {
68e41f4b71Sopenharmony_ci      result.push(this[i] - rhs[i]);
69e41f4b71Sopenharmony_ci    }
70e41f4b71Sopenharmony_ci    return new PointClass(result);
71e41f4b71Sopenharmony_ci  }
72e41f4b71Sopenharmony_ci
73e41f4b71Sopenharmony_ci  multiply(scale: number): PointClass {
74e41f4b71Sopenharmony_ci    let result: Point = new Array<number>() as Point;
75e41f4b71Sopenharmony_ci    for (let i = 0; i < 2; i++) {
76e41f4b71Sopenharmony_ci      result.push(this[i] * scale)
77e41f4b71Sopenharmony_ci    }
78e41f4b71Sopenharmony_ci    return new PointClass(result);
79e41f4b71Sopenharmony_ci  }
80e41f4b71Sopenharmony_ci}
81e41f4b71Sopenharmony_ci
82e41f4b71Sopenharmony_ci// Define the parameter type of the animatable property API and implement the addition, subtraction, multiplication, and equivalence judgment functions in the AnimtableArithmetic<T> API.
83e41f4b71Sopenharmony_ci// Template T supports nested implementation of the AnimtableArithmetic<T> type.
84e41f4b71Sopenharmony_ciclass PointVector extends Array<PointClass> implements AnimatableArithmetic<Array<Point>> {
85e41f4b71Sopenharmony_ci  constructor(initialValue: Array<Point>) {
86e41f4b71Sopenharmony_ci    super();
87e41f4b71Sopenharmony_ci    if (initialValue.length) {
88e41f4b71Sopenharmony_ci      initialValue.forEach((p: Point) => this.push(new PointClass(p)))
89e41f4b71Sopenharmony_ci    }
90e41f4b71Sopenharmony_ci  }
91e41f4b71Sopenharmony_ci
92e41f4b71Sopenharmony_ci  // implement the IAnimatableArithmetic interface
93e41f4b71Sopenharmony_ci  plus(rhs: PointVector): PointVector {
94e41f4b71Sopenharmony_ci    let result = new PointVector([]);
95e41f4b71Sopenharmony_ci    const len = Math.min(this.length, rhs.length)
96e41f4b71Sopenharmony_ci    for (let i = 0; i < len; i++) {
97e41f4b71Sopenharmony_ci      result.push(this[i].add(rhs[i]))
98e41f4b71Sopenharmony_ci    }
99e41f4b71Sopenharmony_ci    return result;
100e41f4b71Sopenharmony_ci  }
101e41f4b71Sopenharmony_ci
102e41f4b71Sopenharmony_ci  subtract(rhs: PointVector): PointVector {
103e41f4b71Sopenharmony_ci    let result = new PointVector([]);
104e41f4b71Sopenharmony_ci    const len = Math.min(this.length, rhs.length)
105e41f4b71Sopenharmony_ci    for (let i = 0; i < len; i++) {
106e41f4b71Sopenharmony_ci      result.push(this[i].subtract(rhs[i]))
107e41f4b71Sopenharmony_ci    }
108e41f4b71Sopenharmony_ci    return result;
109e41f4b71Sopenharmony_ci  }
110e41f4b71Sopenharmony_ci
111e41f4b71Sopenharmony_ci  multiply(scale: number): PointVector {
112e41f4b71Sopenharmony_ci    let result = new PointVector([]);
113e41f4b71Sopenharmony_ci    for (let i = 0; i < this.length; i++) {
114e41f4b71Sopenharmony_ci      result.push(this[i].multiply(scale))
115e41f4b71Sopenharmony_ci    }
116e41f4b71Sopenharmony_ci    return result;
117e41f4b71Sopenharmony_ci  }
118e41f4b71Sopenharmony_ci
119e41f4b71Sopenharmony_ci  equals(rhs: PointVector): boolean {
120e41f4b71Sopenharmony_ci    if (this.length !== rhs.length) {
121e41f4b71Sopenharmony_ci      return false;
122e41f4b71Sopenharmony_ci    }
123e41f4b71Sopenharmony_ci    for (let index = 0, size = this.length; index < size; ++index) {
124e41f4b71Sopenharmony_ci      if (this[index][0] !== rhs[index][0] || this[index][1] !== rhs[index][1]) {
125e41f4b71Sopenharmony_ci        return false;
126e41f4b71Sopenharmony_ci      }
127e41f4b71Sopenharmony_ci    }
128e41f4b71Sopenharmony_ci    return true;
129e41f4b71Sopenharmony_ci  }
130e41f4b71Sopenharmony_ci}
131e41f4b71Sopenharmony_ci
132e41f4b71Sopenharmony_ci// Define a custom animatable property API.
133e41f4b71Sopenharmony_ci@AnimatableExtend(Polyline)
134e41f4b71Sopenharmony_cifunction animatablePoints(points: PointVector) {
135e41f4b71Sopenharmony_ci  .points(points)
136e41f4b71Sopenharmony_ci}
137e41f4b71Sopenharmony_ci
138e41f4b71Sopenharmony_ci@Entry
139e41f4b71Sopenharmony_ci@Component
140e41f4b71Sopenharmony_cistruct AnimatedShape {
141e41f4b71Sopenharmony_ci  squareStartPointX: number = 75;
142e41f4b71Sopenharmony_ci  squareStartPointY: number = 25;
143e41f4b71Sopenharmony_ci  squareWidth: number = 150;
144e41f4b71Sopenharmony_ci  squareEndTranslateX: number = 50;
145e41f4b71Sopenharmony_ci  squareEndTranslateY: number = 50;
146e41f4b71Sopenharmony_ci  @State pointVec1: PointVector = new PointVector([
147e41f4b71Sopenharmony_ci    [this.squareStartPointX, this.squareStartPointY],
148e41f4b71Sopenharmony_ci    [this.squareStartPointX + this.squareWidth, this.squareStartPointY],
149e41f4b71Sopenharmony_ci    [this.squareStartPointX + this.squareWidth, this.squareStartPointY + this.squareWidth],
150e41f4b71Sopenharmony_ci    [this.squareStartPointX, this.squareStartPointY + this.squareWidth]
151e41f4b71Sopenharmony_ci  ]);
152e41f4b71Sopenharmony_ci  @State pointVec2: PointVector = new PointVector([
153e41f4b71Sopenharmony_ci    [this.squareStartPointX + this.squareEndTranslateX, this.squareStartPointY + this.squareStartPointY],
154e41f4b71Sopenharmony_ci    [this.squareStartPointX + this.squareWidth + this.squareEndTranslateX, this.squareStartPointY + this.squareStartPointY],
155e41f4b71Sopenharmony_ci    [this.squareStartPointX + this.squareWidth, this.squareStartPointY + this.squareWidth],
156e41f4b71Sopenharmony_ci    [this.squareStartPointX, this.squareStartPointY + this.squareWidth]
157e41f4b71Sopenharmony_ci  ]);
158e41f4b71Sopenharmony_ci  @State color: Color = Color.Green;
159e41f4b71Sopenharmony_ci  @State fontSize: number = 20.0;
160e41f4b71Sopenharmony_ci  @State polyline1Vec: PointVector = this.pointVec1;
161e41f4b71Sopenharmony_ci  @State polyline2Vec: PointVector = this.pointVec2;
162e41f4b71Sopenharmony_ci
163e41f4b71Sopenharmony_ci  build() {
164e41f4b71Sopenharmony_ci    Row() {
165e41f4b71Sopenharmony_ci      Polyline()
166e41f4b71Sopenharmony_ci        .width(300)
167e41f4b71Sopenharmony_ci        .height(200)
168e41f4b71Sopenharmony_ci        .backgroundColor("#0C000000")
169e41f4b71Sopenharmony_ci        .fill('#317AF7')
170e41f4b71Sopenharmony_ci        .animatablePoints(this.polyline1Vec)
171e41f4b71Sopenharmony_ci        .animation({ duration: 2000, delay: 0, curve: Curve.Ease })
172e41f4b71Sopenharmony_ci        .onClick(() => {
173e41f4b71Sopenharmony_ci
174e41f4b71Sopenharmony_ci          if (this.polyline1Vec.equals(this.pointVec1)) {
175e41f4b71Sopenharmony_ci            this.polyline1Vec = this.pointVec2;
176e41f4b71Sopenharmony_ci          } else {
177e41f4b71Sopenharmony_ci            this.polyline1Vec = this.pointVec1;
178e41f4b71Sopenharmony_ci          }
179e41f4b71Sopenharmony_ci        })
180e41f4b71Sopenharmony_ci    }
181e41f4b71Sopenharmony_ci    .width('100%').height('100%').justifyContent(FlexAlign.Center)
182e41f4b71Sopenharmony_ci  }
183e41f4b71Sopenharmony_ci}
184e41f4b71Sopenharmony_ci```
185e41f4b71Sopenharmony_ci
186e41f4b71Sopenharmony_ci
187e41f4b71Sopenharmony_ci![en-us_image_0000001592669598](figures/en-us_image_0000001592669598.gif)
188