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 plus(rhs: Point): Point { 87 return new Point(this.x + rhs.x, this.y + rhs.y) 88 } 89 subtract(rhs: Point): Point { 90 return new Point(this.x - rhs.x, this.y - rhs.y) 91 } 92 multiply(scale: number): Point { 93 return new Point(this.x * scale, this.y * scale) 94 } 95 equals(rhs: Point): boolean { 96 return this.x === rhs.x && this.y === rhs.y 97 } 98} 99 100// PointVector实现了AnimatableArithmetic<T>接口 101class PointVector extends Array<Point> implements AnimatableArithmetic<PointVector> { 102 constructor(value: Array<Point>) { 103 super(); 104 value.forEach(p => this.push(p)) 105 } 106 plus(rhs: PointVector): PointVector { 107 let result = new PointVector([]) 108 const len = Math.min(this.length, rhs.length) 109 for (let i = 0; i < len; i++) { 110 result.push((this as Array<Point>)[i].plus((rhs as Array<Point>)[i])) 111 } 112 return result 113 } 114 subtract(rhs: PointVector): PointVector { 115 let result = new PointVector([]) 116 const len = Math.min(this.length, rhs.length) 117 for (let i = 0; i < len; i++) { 118 result.push((this as Array<Point>)[i].subtract((rhs as Array<Point>)[i])) 119 } 120 return result 121 } 122 multiply(scale: number): PointVector { 123 let result = new PointVector([]) 124 for (let i = 0; i < this.length; i++) { 125 result.push((this as Array<Point>)[i].multiply(scale)) 126 } 127 return result 128 } 129 equals(rhs: PointVector): boolean { 130 if (this.length != rhs.length) { 131 return false 132 } 133 for (let i = 0; i < this.length; i++) { 134 if (!(this as Array<Point>)[i].equals((rhs as Array<Point>)[i])) { 135 return false 136 } 137 } 138 return true 139 } 140 get(): Array<Object[]> { 141 let result: Array<Object[]> = [] 142 this.forEach(p => result.push([p.x, p.y])) 143 return result 144 } 145} 146 147@AnimatableExtend(Polyline) function animatablePoints(points: PointVector) { 148 .points(points.get()) 149} 150 151@Entry 152@Component 153struct AnimatablePropertyExample { 154 @State points: PointVector = new PointVector([ 155 new Point(50, Math.random() * 200), 156 new Point(100, Math.random() * 200), 157 new Point(150, Math.random() * 200), 158 new Point(200, Math.random() * 200), 159 new Point(250, Math.random() * 200), 160 ]) 161 build() { 162 Column() { 163 Polyline() 164 .animatablePoints(this.points) 165 .animation({duration: 1000, curve: Curve.Ease}) // 设置动画参数 166 .size({height:220, width:300}) 167 .fill(Color.Green) 168 .stroke(Color.Red) 169 .backgroundColor('#eeaacc') 170 Button("Play") 171 .onClick(() => { 172 // points是实现了可动画协议的数据类型,points在动画过程中可按照定义的运算规则、动画参数从之前的PointVector变为新的PointVector数据,产生每一帧的PointVector数据,进而产生动画 173 this.points = new PointVector([ 174 new Point(50, Math.random() * 200), 175 new Point(100, Math.random() * 200), 176 new Point(150, Math.random() * 200), 177 new Point(200, Math.random() * 200), 178 new Point(250, Math.random() * 200), 179 ]) 180 }) 181 }.width("100%") 182 .padding(10) 183 } 184} 185``` 186