1# 自定义属性动画 2 3 4属性动画是可动画属性的参数值发生变化时,引起UI上产生的连续视觉效果。当参数值发生连续变化,且设置到可以引起UI发生变化的属性接口上时,就可以实现属性动画。 5 6 7ArkUI提供[@AnimatableExtend](../quick-start/arkts-animatable-extend.md)装饰器,用于自定义可动画属性接口。由于参数的数据类型必须具备一定程度的连续性,自定义可动画属性接口的参数类型仅支持number类型和实现[AnimtableArithmetic\<T>](../quick-start/arkts-animatable-extend.md)接口的自定义类型。通过自定义可动画属性接口和可动画数据类型,在使用animateTo或animation执行动画时,通过逐帧回调函数修改不可动画属性接口的值,能够让不可动画属性接口实现动画效果。 8 9 10## 使用number数据类型和\@AnimatableExtend装饰器改变字体大小 11 12 13```ts 14// 第一步:使用@AnimatableExtend装饰器,自定义可动画属性接口 15@AnimatableExtend(Text) 16function animatableFontSize(size: number) { 17 .fontSize(size) // 调用系统属性接口 18} 19 20@Entry 21@Component 22struct AnimatablePropertyExample { 23 @State fontSize: number = 20; 24 25 build() { 26 Row() { 27 Text("AnimatableProperty") 28 .backgroundColor("#0C000000") 29 .animatableFontSize(this.fontSize)// 第二步:将自定义可动画属性接口设置到组件上 30 .animation({ duration: 1000, curve: "ease" })// 第三步:为自定义可动画属性接口绑定动画 31 .width(300) 32 .height(140) 33 .textAlign(TextAlign.Center) 34 .onClick(() => { 35 this.fontSize = this.fontSize == 20 ? 30 : 20; // 第四步:改变自定义可动画属性的参数,产生动画 36 }) 37 }.width("100%").height('100%').justifyContent(FlexAlign.Center) 38 .padding(10) 39 } 40} 41``` 42 43 44 45 46 47 48## 使用自定义数据类型和\@AnimatableExtend装饰器改变图形形状 49 50 51```ts 52declare type Point = number[]; 53 54// 定义可动画属性接口的参数类型,实现AnimtableArithmetic<T>接口中加法、减法、乘法和判断相等函数 55class PointClass extends Array<number> { 56 constructor(value: Point) { 57 super(value[0], value[1]) 58 } 59 60 add(rhs: PointClass): PointClass { 61 let result: Point = new Array<number>() as Point; 62 for (let i = 0; i < 2; i++) { 63 result.push(rhs[i] + this[i]) 64 } 65 return new PointClass(result); 66 } 67 68 subtract(rhs: PointClass): PointClass { 69 let result: Point = new Array<number>() as Point; 70 for (let i = 0; i < 2; i++) { 71 result.push(this[i] - rhs[i]); 72 } 73 return new PointClass(result); 74 } 75 76 multiply(scale: number): PointClass { 77 let result: Point = new Array<number>() as Point; 78 for (let i = 0; i < 2; i++) { 79 result.push(this[i] * scale) 80 } 81 return new PointClass(result); 82 } 83} 84 85// 定义可动画属性接口的参数类型,实现AnimtableArithmetic<T>接口中加法、减法、乘法和判断相等函数 86// 模板T支持嵌套实现AnimtableArithmetic<T>的类型 87class PointVector extends Array<PointClass> implements AnimatableArithmetic<Array<Point>> { 88 constructor(initialValue: Array<Point>) { 89 super(); 90 if (initialValue.length) { 91 initialValue.forEach((p: Point) => this.push(new PointClass(p))) 92 } 93 } 94 95 // implement the IAnimatableArithmetic interface 96 plus(rhs: PointVector): PointVector { 97 let result = new PointVector([]); 98 const len = Math.min(this.length, rhs.length) 99 for (let i = 0; i < len; i++) { 100 result.push(this[i].add(rhs[i])) 101 } 102 return result; 103 } 104 105 subtract(rhs: PointVector): PointVector { 106 let result = new PointVector([]); 107 const len = Math.min(this.length, rhs.length) 108 for (let i = 0; i < len; i++) { 109 result.push(this[i].subtract(rhs[i])) 110 } 111 return result; 112 } 113 114 multiply(scale: number): PointVector { 115 let result = new PointVector([]); 116 for (let i = 0; i < this.length; i++) { 117 result.push(this[i].multiply(scale)) 118 } 119 return result; 120 } 121 122 equals(rhs: PointVector): boolean { 123 if (this.length !== rhs.length) { 124 return false; 125 } 126 for (let index = 0, size = this.length; index < size; ++index) { 127 if (this[index][0] !== rhs[index][0] || this[index][1] !== rhs[index][1]) { 128 return false; 129 } 130 } 131 return true; 132 } 133} 134 135// 自定义可动画属性接口 136@AnimatableExtend(Polyline) 137function animatablePoints(points: PointVector) { 138 .points(points) 139} 140 141@Entry 142@Component 143struct AnimatedShape { 144 squareStartPointX: number = 75; 145 squareStartPointY: number = 25; 146 squareWidth: number = 150; 147 squareEndTranslateX: number = 50; 148 squareEndTranslateY: number = 50; 149 @State pointVec1: PointVector = new PointVector([ 150 [this.squareStartPointX, this.squareStartPointY], 151 [this.squareStartPointX + this.squareWidth, this.squareStartPointY], 152 [this.squareStartPointX + this.squareWidth, this.squareStartPointY + this.squareWidth], 153 [this.squareStartPointX, this.squareStartPointY + this.squareWidth] 154 ]); 155 @State pointVec2: PointVector = new PointVector([ 156 [this.squareStartPointX + this.squareEndTranslateX, this.squareStartPointY + this.squareStartPointY], 157 [this.squareStartPointX + this.squareWidth + this.squareEndTranslateX, this.squareStartPointY + this.squareStartPointY], 158 [this.squareStartPointX + this.squareWidth, this.squareStartPointY + this.squareWidth], 159 [this.squareStartPointX, this.squareStartPointY + this.squareWidth] 160 ]); 161 @State color: Color = Color.Green; 162 @State fontSize: number = 20.0; 163 @State polyline1Vec: PointVector = this.pointVec1; 164 @State polyline2Vec: PointVector = this.pointVec2; 165 166 build() { 167 Row() { 168 Polyline() 169 .width(300) 170 .height(200) 171 .backgroundColor("#0C000000") 172 .fill('#317AF7') 173 .animatablePoints(this.polyline1Vec) 174 .animation({ duration: 2000, delay: 0, curve: Curve.Ease }) 175 .onClick(() => { 176 177 if (this.polyline1Vec.equals(this.pointVec1)) { 178 this.polyline1Vec = this.pointVec2; 179 } else { 180 this.polyline1Vec = this.pointVec1; 181 } 182 }) 183 } 184 .width('100%').height('100%').justifyContent(FlexAlign.Center) 185 } 186} 187``` 188 189 190 191 192## 相关实例 193 194针对自定义属性动画开发,有以下相关实例可供参考: 195 196- [自定义下拉刷新动画(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/ETSUI/AnimateRefresh)