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