1# 定义可动画属性 (@AnimatableExtend) 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @hehongyang3--> 5<!--Designer: @yangfan229--> 6<!--Tester: @lxl007--> 7<!--Adviser: @HelloCrease--> 8 9@AnimatableExtend装饰器用于自定义可动画的属性方法,该装饰器内定义的函数在动画过程中会被逐帧调用,直到动画结束。该装饰器的常见用途有: 10 111. 使不可动画属性变为可动画属性,自定义数据运算规则使得属性能进行插值运算得到中间结果,再由动画驱动属性从起点值逐渐过渡到终点值。 12 132. 使属性逐帧变化,实现逐帧布局的效果。 14 15- 可动画属性:如果一个属性方法在animation属性前调用,改变这个属性的值可以使animation属性的动画效果生效,属性有动画过渡效果,这个属性称为可动画属性。比如height、width、backgroundColor、translate属性,和Text组件的fontSize属性等。 16 17- 不可动画属性:如果一个属性方法在animation属性前调用,改变这个属性的值不能使animation属性的动画效果生效,属性突变无动画效果,这个属性称为不可动画属性。比如Polyline组件的points属性等。 18 19> **说明:** 20> 21> 该装饰器从API version 10开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 22 23## 语法 24 25```ts 26@AnimatableExtend(UIComponentName) function functionName(value: typeName) { 27 .propertyName(value) 28} 29``` 30 31- \@AnimatableExtend仅支持定义在全局,不支持在组件内部定义。 32- \@AnimatableExtend定义的函数参数类型必须为number类型或者实现 AnimatableArithmetic\<T\>接口的自定义类型。 33- \@AnimatableExtend定义的函数体内只能调用\@AnimatableExtend括号内组件的属性方法。 34 35## AnimatableArithmetic\<T\> 36 37该接口定义非number数据类型的动画运算规则。对非number类型的数据(如数组、结构体、颜色等)做动画,需要实现AnimatableArithmetic\<T\>接口中加法、减法、乘法和判断相等函数, 38使得该数据能参与动画的插值运算和识别该数据是否发生改变。即定义它们为实现了AnimatableArithmetic\<T\>接口的类型。 39 40**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 41 42**系统能力:** SystemCapability.ArkUI.ArkUI.Full 43 44### plus 45 46plus(rhs: AnimatableArithmetic\<T\>): AnimatableArithmetic\<T\> 47 48定义数据类型的加法运算规则。 49 50**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 51 52**系统能力:** SystemCapability.ArkUI.ArkUI.Full 53 54**参数:** 55 56| 参数名 | 类型 | 必填 | 说明 | 57| ----- | --------------------------------- | ---- | ------------------------------------- | 58| rhs | [AnimatableArithmetic\<T\>](#animatablearithmetict) | 是 | 加法运算的对象。 | 59 60**返回值:** 61 62| 类型 | 说明 | 63| ---------------------------------------- | ------- | 64| [AnimatableArithmetic\<T\>](#animatablearithmetict) | 加法运算的结果。 | 65 66### subtract 67 68subtract(rhs: AnimatableArithmetic\<T\>): AnimatableArithmetic\<T\> 69 70定义该数据类型的减法运算规则。 71 72**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 73 74**系统能力:** SystemCapability.ArkUI.ArkUI.Full 75 76**参数:** 77 78| 参数名 | 类型 | 必填 | 说明 | 79| ----- | --------------------------------- | ---- | ------------------------------------- | 80| rhs | [AnimatableArithmetic\<T\>](#animatablearithmetict) | 是 | 减法运算的对象。 | 81 82**返回值:** 83 84| 类型 | 说明 | 85| ---------------------------------------- | ------- | 86| [AnimatableArithmetic\<T\>](#animatablearithmetict) | 减法运算的结果。 | 87 88### multiply 89 90multiply(scale: number): AnimatableArithmetic\<T\> 91 92定义该数据类型的乘法运算规则。 93 94**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 95 96**系统能力:** SystemCapability.ArkUI.ArkUI.Full 97 98**参数:** 99 100| 参数名 | 类型 | 必填 | 说明 | 101| ----- | --------------------------------- | ---- | ------------------------------------- | 102| scale | number | 是 | 乘法运算的系数。 | 103 104**返回值:** 105 106| 类型 | 说明 | 107| ---------------------------------------- | ------- | 108| [AnimatableArithmetic\<T\>](#animatablearithmetict) | 乘法运算的结果。 | 109 110### equals 111 112equals(rhs: AnimatableArithmetic\<T\>): boolean 113 114定义该数据类型的相等判断规则。 115 116**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 117 118**系统能力:** SystemCapability.ArkUI.ArkUI.Full 119 120**参数:** 121 122| 参数名 | 类型 | 必填 | 说明 | 123| ----- | --------------------------------- | ---- | ------------------------------------- | 124| rhs | [AnimatableArithmetic\<T\>](#animatablearithmetict) | 是 | 和自身比较相等的另一个数据对象。 | 125 126**返回值:** 127 128| 类型 | 说明 | 129| ---------------------------------------- | ------- | 130| boolean | 是否相等。返回true表示相等,返回false表示不相等。 | 131 132## 示例 133 134### 示例1(逐帧布局的效果) 135 136以下示例通过改变Text组件宽度实现逐帧布局的效果。 137 138```ts 139@AnimatableExtend(Text) 140function animatableWidth(width: number) { 141 .width(width) 142} 143 144@Entry 145@Component 146struct AnimatablePropertyExample { 147 @State textWidth: number = 80; 148 149 build() { 150 Column() { 151 Text("AnimatableProperty") 152 .animatableWidth(this.textWidth) 153 .animation({ duration: 2000, curve: Curve.Ease }) 154 Button("Play") 155 .onClick(() => { 156 this.textWidth = this.textWidth == 80 ? 160 : 80; 157 }) 158 }.width("100%") 159 .padding(10) 160 } 161} 162``` 163 164 165 166### 示例2(折线的动画效果) 167 168以下示例实现折线的动画效果。 169 170```ts 171class Point { 172 x: number 173 y: number 174 175 constructor(x: number, y: number) { 176 this.x = x 177 this.y = y 178 } 179 180 plus(rhs: Point): Point { 181 return new Point(this.x + rhs.x, this.y + rhs.y); 182 } 183 184 subtract(rhs: Point): Point { 185 return new Point(this.x - rhs.x, this.y - rhs.y); 186 } 187 188 multiply(scale: number): Point { 189 return new Point(this.x * scale, this.y * scale); 190 } 191 192 equals(rhs: Point): boolean { 193 return this.x === rhs.x && this.y === rhs.y; 194 } 195} 196 197// PointVector实现了AnimatableArithmetic<T>接口 198class PointVector extends Array<Point> implements AnimatableArithmetic<PointVector> { 199 constructor(value: Array<Point>) { 200 super(); 201 value.forEach(p => this.push(p)); 202 } 203 204 plus(rhs: PointVector): PointVector { 205 let result = new PointVector([]); 206 const len = Math.min(this.length, rhs.length); 207 for (let i = 0; i < len; i++) { 208 result.push((this as Array<Point>)[i].plus((rhs as Array<Point>)[i])); 209 } 210 return result; 211 } 212 213 subtract(rhs: PointVector): PointVector { 214 let result = new PointVector([]); 215 const len = Math.min(this.length, rhs.length); 216 for (let i = 0; i < len; i++) { 217 result.push((this as Array<Point>)[i].subtract((rhs as Array<Point>)[i])); 218 } 219 return result; 220 } 221 222 multiply(scale: number): PointVector { 223 let result = new PointVector([]); 224 for (let i = 0; i < this.length; i++) { 225 result.push((this as Array<Point>)[i].multiply(scale)); 226 } 227 return result; 228 } 229 230 equals(rhs: PointVector): boolean { 231 if (this.length != rhs.length) { 232 return false; 233 } 234 for (let i = 0; i < this.length; i++) { 235 if (!(this as Array<Point>)[i].equals((rhs as Array<Point>)[i])) { 236 return false; 237 } 238 } 239 return true; 240 } 241 242 get(): Array<Object[]> { 243 let result: Array<Object[]> = []; 244 this.forEach(p => result.push([p.x, p.y])); 245 return result; 246 } 247} 248 249@AnimatableExtend(Polyline) 250function animatablePoints(points: PointVector) { 251 .points(points.get()) 252} 253 254@Entry 255@Component 256struct AnimatablePropertyExample { 257 @State points: PointVector = new PointVector([ 258 new Point(50, Math.random() * 200), 259 new Point(100, Math.random() * 200), 260 new Point(150, Math.random() * 200), 261 new Point(200, Math.random() * 200), 262 new Point(250, Math.random() * 200), 263 ]) 264 265 build() { 266 Column() { 267 Polyline() 268 .animatablePoints(this.points) 269 .animation({ duration: 1000, curve: Curve.Ease })// 设置动画参数 270 .size({ height: 220, width: 300 }) 271 .fill(Color.Green) 272 .stroke(Color.Red) 273 .backgroundColor('#eeaacc') 274 Button("Play") 275 .onClick(() => { 276 // points是实现了可动画协议的数据类型,points在动画过程中可按照定义的运算规则、动画参数从之前的PointVector变为新的PointVector数据,产生每一帧的PointVector数据,进而产生动画 277 this.points = new PointVector([ 278 new Point(50, Math.random() * 200), 279 new Point(100, Math.random() * 200), 280 new Point(150, Math.random() * 200), 281 new Point(200, Math.random() * 200), 282 new Point(250, Math.random() * 200), 283 ]); 284 }) 285 }.width("100%") 286 .padding(10) 287 } 288} 289``` 290