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