1# \@AnimatableExtend Decorator: Definition of Animatable Attributes
2
3The @AnimatableExtend decorator is used to define an attribute method for the non-animatable attribute of a component. During animation execution, a frame-by-frame callback is used to change the value of the non-animatable attribute so that an animation effect can be applied to the attribute. Additionally, you can implement frame-by-frame layout effects by modifying the values of animatable properties in the per-frame callback function.
4
5- Animatable attribute: If an attribute method is called before the **animation** attribute, and changing the value of this attribute can make the animation effect specified by the **animation** attribute take effect, then this attribute is called animatable attribute. For example, **height**, **width**, **backgroundColor**, **translate**, and **fontSize** (of the **Text** component) are all animatable attributes.
6
7- Non-animatable attribute: If an attribute method is called before the **animation** attribute, and changing the value of this attribute cannot make the animation effect specified by the **animation** attribute take effect, then this attribute is called non-animatable attribute. For example, the **points** attribute of the **Polyline** component is a non-animatable attribute.
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## Rules of Use
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- In the \@AnimatableExtend decorated function body, only the attribute methods of the component specified in brackets immediately following \@AnimatableExtend can be called.
30
31### Available APIs
32To perform animation when complex data types are involved, you must implement the addition, subtraction, multiplication, and equivalence judgment functions in the **AnimatableArithmetic\<T\>** API.
33| Name| Input Parameter Type| Return Value Type | Description |
34| -------- | --------  |--------   |--------  |
35| plus | AnimatableArithmetic\<T\> | AnimatableArithmetic\<T\> | Addition function.|
36| subtract | AnimatableArithmetic\<T\> | AnimatableArithmetic\<T\> | Subtraction function.|
37| multiply | number | AnimatableArithmetic\<T\> | Multiplication function.|
38| equals | AnimatableArithmetic\<T\> | boolean | Equivalence judgment function.|
39
40## Example
41
42The following example implements the frame-by-frame layout effects by changing the width of the **Text** component.
43
44
45```ts
46@AnimatableExtend(Text)
47function animatableWidth(width: number) {
48  .width(width)
49}
50
51@Entry
52@Component
53struct AnimatablePropertyExample {
54  @State textWidth: number = 80;
55
56  build() {
57    Column() {
58      Text("AnimatableProperty")
59        .animatableWidth(this.textWidth)
60        .animation({ duration: 2000, curve: Curve.Ease })
61      Button("Play")
62        .onClick(() => {
63          this.textWidth = this.textWidth == 80 ? 160 : 80;
64        })
65    }.width("100%")
66    .padding(10)
67  }
68}
69```
70![image](figures/AnimatableProperty.gif)
71
72
73The following example implements a polyline animation effect.
74
75
76```ts
77class Point {
78  x: number
79  y: number
80
81  constructor(x: number, y: number) {
82    this.x = x
83    this.y = y
84  }
85  plus(rhs: Point): Point {
86    return new Point(this.x + rhs.x, this.y + rhs.y)
87  }
88  subtract(rhs: Point): Point {
89    return new Point(this.x - rhs.x, this.y - rhs.y)
90  }
91  multiply(scale: number): Point {
92    return new Point(this.x * scale, this.y * scale)
93  }
94  equals(rhs: Point): boolean {
95    return this.x === rhs.x && this.y === rhs.y
96  }
97}
98
99class PointVector extends Array<Point> implements AnimatableArithmetic<PointVector> {
100  constructor(value: Array<Point>) {
101    super();
102    value.forEach(p => this.push(p))
103  }
104  plus(rhs: PointVector): PointVector {
105    let result = new PointVector([])
106    const len = Math.min(this.length, rhs.length)
107    for (let i = 0; i < len; i++) {
108      result.push((this as Array<Point>)[i].plus((rhs as Array<Point>)[i]))
109    }
110    return result
111  }
112  subtract(rhs: PointVector): PointVector {
113    let result = new PointVector([])
114    const len = Math.min(this.length, rhs.length)
115    for (let i = 0; i < len; i++) {
116      result.push((this as Array<Point>)[i].subtract((rhs as Array<Point>)[i]))
117    }
118    return result
119  }
120  multiply(scale: number): PointVector {
121    let result = new PointVector([])
122    for (let i = 0; i < this.length; i++) {
123      result.push((this as Array<Point>)[i].multiply(scale))
124    }
125    return result
126  }
127  equals(rhs: PointVector): boolean {
128    if (this.length != rhs.length) {
129      return false
130    }
131    for (let i = 0; i < this.length; i++) {
132      if (!(this as Array<Point>)[i].equals((rhs as Array<Point>)[i])) {
133        return false
134      }
135    }
136    return true
137  }
138  get(): Array<Object[]> {
139    let result: Array<Object[]> = []
140    this.forEach(p => result.push([p.x, p.y]))
141    return result
142  }
143}
144
145@AnimatableExtend(Polyline) function animatablePoints(points: PointVector) {
146  .points(points.get())
147}
148
149@Entry
150@Component
151struct AnimatablePropertyExample {
152  @State points: PointVector = new PointVector([
153    new Point(50, Math.random() * 200),
154    new Point(100, Math.random() * 200),
155    new Point(150, Math.random() * 200),
156    new Point(200, Math.random() * 200),
157    new Point(250, Math.random() * 200),
158  ])
159  build() {
160    Column() {
161      Polyline()
162        .animatablePoints(this.points)
163        .animation({duration: 1000, curve: Curve.Ease})
164        .size({height:220, width:300})
165        .fill(Color.Green)
166        .stroke(Color.Red)
167        .backgroundColor('#eeaacc')
168      Button("Play")
169        .onClick(() => {
170          this.points = new PointVector([
171            new Point(50, Math.random() * 200),
172            new Point(100, Math.random() * 200),
173            new Point(150, Math.random() * 200),
174            new Point(200, Math.random() * 200),
175            new Point(250, Math.random() * 200),
176          ])
177        })
178    }.width("100%")
179    .padding(10)
180  }
181}
182```
183![image](figures/animatable-points.gif)
184