• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![zh-cn_image_0000001600119626](figures/zh-cn_image_0000001600119626.gif)
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![zh-cn_image_0000001592669598](figures/zh-cn_image_0000001592669598.gif)
191
192## 相关实例
193
194针对自定义属性动画开发,有以下相关实例可供参考:
195
196- [自定义下拉刷新动画(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/ETSUI/AnimateRefresh)