• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![image](figures/AnimatableProperty.gif)
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![image](figures/animatable-points.gif)