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