1# 自定义属性动画 2 3 4属性动画是可动画属性的参数值发生变化时,引起UI上产生的连续视觉效果。当参数值发生连续变化,且设置到可以引起UI发生变化的属性接口上时,就可以实现属性动画。 5 6 7ArkUI提供[@AnimatableExtend装饰器](../quick-start/arkts-animatable-extend.md),用于自定义可动画属性接口。由于参数的数据类型必须具备一定程度的连续性,自定义可动画属性接口的参数类型仅支持number类型和实现[AnimatableArithmetic\<T>接口](../quick-start/arkts-animatable-extend.md#animatablearithmetict接口说明)的自定义类型。通过自定义可动画属性接口和可动画数据类型,在使用animateTo或animation执行动画时,通过逐帧回调函数修改不可动画属性接口的值,能够让不可动画属性接口实现动画效果。也可通过逐帧回调函数每帧修改可动画属性的值,实现逐帧布局的效果。 8 9 10## 使用number数据类型和\@AnimatableExtend装饰器改变Text组件宽度实现逐帧布局的效果 11 12 13```ts 14// 第一步:使用@AnimatableExtend装饰器,自定义可动画属性接口 15@AnimatableExtend(Text) 16function animatableWidth(width: number) { 17 .width(width)// 调用系统属性接口,逐帧回调函数每帧修改可动画属性的值,实现逐帧布局的效果。 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)// 第二步:将自定义可动画属性接口设置到组件上 29 .animation({ duration: 2000, curve: Curve.Ease })// 第三步:为自定义可动画属性接口绑定动画 30 Button("Play") 31 .onClick(() => { 32 this.textWidth = this.textWidth == 80 ? 160 : 80;// 第四步:改变自定义可动画属性的参数,产生动画 33 }) 34 }.width("100%") 35 .padding(10) 36 } 37} 38``` 39 40 41 42 43 44 45## 使用自定义数据类型和\@AnimatableExtend装饰器改变图形形状 46 47 48```ts 49declare type Point = number[]; 50 51// 定义可动画属性接口的参数类型,实现AnimatableArithmetic<T>接口中加法、减法、乘法和判断相等函数 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// 定义可动画属性接口的参数类型,实现AnimatableArithmetic<T>接口中加法、减法、乘法和判断相等函数 83// 模板T支持嵌套实现AnimatableArithmetic<T>的类型 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// 自定义可动画属性接口 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 189## 相关实例 190 191针对自定义属性动画开发,有以下相关实例可供参考: 192 193- [自定义下拉刷新动画(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/ETSUI/AnimateRefresh)