1# \@AnimatableExtend装饰器:定义可动画属性 2 3@AnimatableExtend装饰器用于自定义可动画的属性方法,在这个属性方法中修改组件不可动画的属性。在动画执行过程时,通过逐帧回调函数修改不可动画属性值,让不可动画属性也能实现动画效果。也可通过逐帧回调函数修改可动画属性的值,实现逐帧布局的效果。 4 5- 可动画属性:如果一个属性方法在animation属性前调用,改变这个属性的值可以使animation属性的动画效果生效,这个属性称为可动画属性。比如height、width、backgroundColor、translate属性,和Text组件的fontSize属性等。 6 7- 不可动画属性:如果一个属性方法在animation属性前调用,改变这个属性的值不能使animation属性的动画效果生效,这个属性称为不可动画属性。比如Polyline组件的points属性等。 8 9> **说明:** 10> 11> 该装饰器从API Version 10开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 12> 13> 从API version 11开始,该装饰器支持在原子化服务中使用。 14 15## 装饰器使用说明 16 17 18### 语法 19 20 21```ts 22@AnimatableExtend(UIComponentName) function functionName(value: typeName) { 23 .propertyName(value) 24} 25``` 26 27- \@AnimatableExtend仅支持定义在全局,不支持在组件内部定义。 28- \@AnimatableExtend定义的函数参数类型必须为number类型或者实现 AnimatableArithmetic\<T\>接口的自定义类型。 29- \@AnimatableExtend定义的函数体内只能调用\@AnimatableExtend括号内组件的属性方法。 30 31### AnimatableArithmetic\<T\>接口说明 32该接口定义非number数据类型的动画运算规则。对非number类型的数据(如数组、结构体、颜色等)做动画,需要实现AnimatableArithmetic\<T\>接口中加法、减法、乘法和判断相等函数, 33使得该数据能参与动画的插值运算和识别该数据是否发生改变。即定义它们为实现了AnimatableArithmetic\<T\>接口的类型。 34| 名称 | 入参类型 | 返回值类型 | 说明 35| -------- | -------- |-------- |-------- | 36| plus | AnimatableArithmetic\<T\> | AnimatableArithmetic\<T\> | 定义该数据类型的加法运算规则 | 37| subtract | AnimatableArithmetic\<T\> | AnimatableArithmetic\<T\> | 定义该数据类型的减法运算规则 | 38| multiply | number | AnimatableArithmetic\<T\> | 定义该数据类型的乘法运算规则 | 39| equals | AnimatableArithmetic\<T\> | boolean | 定义该数据类型的相等判断规则| 40 41## 使用场景 42 43以下示例通过改变Text组件宽度实现逐帧布局的效果。 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 74以下示例实现折线的动画效果。 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实现了AnimatableArithmetic<T>接口 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 })// 设置动画参数 177 .size({ height: 220, width: 300 }) 178 .fill(Color.Green) 179 .stroke(Color.Red) 180 .backgroundColor('#eeaacc') 181 Button("Play") 182 .onClick(() => { 183 // points是实现了可动画协议的数据类型,points在动画过程中可按照定义的运算规则、动画参数从之前的PointVector变为新的PointVector数据,产生每一帧的PointVector数据,进而产生动画 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