• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# \@AnimatableExtend Decorator: Defining Animatable Properties
2
3The @AnimatableExtend decorator enables animation capabilities for normally non-animatable component properties. During animation execution, frame-by-frame callbacks are executed to change the values of non-animatable properties to allow them to achieve animation effects. Additionally, you can implement frame-by-frame layout effects by changing the values of animatable properties in the per-frame callback function.
4
5- Animatable property: A property is considered animatable if, when its method is called before the **animation** attribute, changing its value triggers the animation effect specified by **animation**. Examples include **height**, **width**, **backgroundColor**, **translate**, and **fontSize** (of the **Text** component).
6
7- Non-animatable property: A property is non-animatable if, when its method is called before the **animation** attribute, changing its value does not trigger the animation effect specified by **animation**. For example, the **points** property of the **Polyline** component is a non-animatable.
8
9>  **NOTE**
10>
11>  This decorator is supported since API version 10. Updates will be marked with a superscript to indicate their earliest API version.
12>
13> This decorator can be used in atomic services since API version 11.
14
15## Usage Rules
16
17
18### Syntax
19
20
21```ts
22@AnimatableExtend(UIComponentName) function functionName(value: typeName) {
23  .propertyName(value)
24}
25```
26
27- \@AnimatableExtend can be defined only globally.
28- The parameter of the \@AnimatableExtend decorated function must be of the number type or a custom type that implements the **AnimatableArithmetic\<T\>** API.
29- The function body of an @AnimatableExtend decorated function can only access property methods of the component type specified within the parentheses of @AnimatableExtend.
30
31### Available APIs
32The **AnimatableArithmetic\<T\>** API defines the animation operation rules for non-number data types. To animate non-number data (such as arrays, structs, and colors), implement the addition, subtraction, multiplication, and equality judgment functions in the **AnimatableArithmetic\<T\>** API.
33In this way, the data can be involved in an interpolation operation of the animation and identify whether the data changes, that is, the non-number data is defined as the types that implement the **AnimatableArithmetic\<T\>** API.
34| Name| Input Parameter Type| Return Value Type| Description
35| -------- | -------- |-------- |-------- |
36| plus | AnimatableArithmetic\<T\> | AnimatableArithmetic\<T\> | Defines the addition rule of the data type.|
37| subtract | AnimatableArithmetic\<T\> | AnimatableArithmetic\<T\> | Defines the subtraction rule of the data type.|
38| multiply | number | AnimatableArithmetic\<T\> | Defines the multiplication rule of the data type.|
39| equals | AnimatableArithmetic\<T\> | boolean | Defines the equality judgment rule of the data type.|
40
41## Example
42
43The following example implements the frame-by-frame layout effects by changing the width of the **Text** component.
44
45
46```ts
47@AnimatableExtend(Text)
48function animatableWidth(width: number) {
49  .width(width)
50}
51
52@Entry
53@Component
54struct AnimatablePropertyExample {
55  @State textWidth: number = 80;
56
57  build() {
58    Column() {
59      Text("AnimatableProperty")
60        .animatableWidth(this.textWidth)
61        .animation({ duration: 2000, curve: Curve.Ease })
62      Button("Play")
63        .onClick(() => {
64          this.textWidth = this.textWidth == 80 ? 160 : 80;
65        })
66    }.width("100%")
67    .padding(10)
68  }
69}
70```
71![image](figures/AnimatableProperty.gif)
72
73
74The following example implements a polyline animation effect.
75
76
77```ts
78class Point {
79  x: number
80  y: number
81
82  constructor(x: number, y: number) {
83    this.x = x
84    this.y = y
85  }
86
87  plus(rhs: Point): Point {
88    return new Point(this.x + rhs.x, this.y + rhs.y);
89  }
90
91  subtract(rhs: Point): Point {
92    return new Point(this.x - rhs.x, this.y - rhs.y);
93  }
94
95  multiply(scale: number): Point {
96    return new Point(this.x * scale, this.y * scale);
97  }
98
99  equals(rhs: Point): boolean {
100    return this.x === rhs.x && this.y === rhs.y;
101  }
102}
103
104// PointVector implements the AnimatableArithmetic<T> API.
105class PointVector extends Array<Point> implements AnimatableArithmetic<PointVector> {
106  constructor(value: Array<Point>) {
107    super();
108    value.forEach(p => this.push(p));
109  }
110
111  plus(rhs: PointVector): PointVector {
112    let result = new PointVector([]);
113    const len = Math.min(this.length, rhs.length);
114    for (let i = 0; i < len; i++) {
115      result.push((this as Array<Point>)[i].plus((rhs as Array<Point>)[i]));
116    }
117    return result;
118  }
119
120  subtract(rhs: PointVector): PointVector {
121    let result = new PointVector([]);
122    const len = Math.min(this.length, rhs.length);
123    for (let i = 0; i < len; i++) {
124      result.push((this as Array<Point>)[i].subtract((rhs as Array<Point>)[i]));
125    }
126    return result;
127  }
128
129  multiply(scale: number): PointVector {
130    let result = new PointVector([]);
131    for (let i = 0; i < this.length; i++) {
132      result.push((this as Array<Point>)[i].multiply(scale));
133    }
134    return result;
135  }
136
137  equals(rhs: PointVector): boolean {
138    if (this.length != rhs.length) {
139      return false;
140    }
141    for (let i = 0; i < this.length; i++) {
142      if (!(this as Array<Point>)[i].equals((rhs as Array<Point>)[i])) {
143        return false;
144      }
145    }
146    return true;
147  }
148
149  get(): Array<Object[]> {
150    let result: Array<Object[]> = [];
151    this.forEach(p => result.push([p.x, p.y]));
152    return result;
153  }
154}
155
156@AnimatableExtend(Polyline)
157function animatablePoints(points: PointVector) {
158  .points(points.get())
159}
160
161@Entry
162@Component
163struct AnimatablePropertyExample {
164  @State points: PointVector = new PointVector([
165    new Point(50, Math.random() * 200),
166    new Point(100, Math.random() * 200),
167    new Point(150, Math.random() * 200),
168    new Point(200, Math.random() * 200),
169    new Point(250, Math.random() * 200),
170  ])
171
172  build() {
173    Column() {
174      Polyline()
175        .animatablePoints(this.points)
176        .animation({ duration: 1000, curve: Curve.Ease })// Set animation parameters.
177        .size({ height: 220, width: 300 })
178        .fill(Color.Green)
179        .stroke(Color.Red)
180        .backgroundColor('#eeaacc')
181      Button("Play")
182        .onClick(() => {
183          // points is a data type that implements the animation protocol. During the animation, points can be changed from the previous PointVector data to the new one based on the defined operation rules and animation parameters to generate the PointVector data of each frame and then generate an animation.
184          this.points = new PointVector([
185            new Point(50, Math.random() * 200),
186            new Point(100, Math.random() * 200),
187            new Point(150, Math.random() * 200),
188            new Point(200, Math.random() * 200),
189            new Point(250, Math.random() * 200),
190          ]);
191        })
192    }.width("100%")
193    .padding(10)
194  }
195}
196```
197![image](figures/animatable-points.gif)
198