• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# \@AnimatableExtend Decorator: Definition of Animatable Attributes
2
3The @AnimatableExtend decorator is used to define an attribute method for the non-animatable attribute of a component. During animation execution, a frame-by-frame callback is used to change the value of the non-animatable attribute so that an animation effect can be applied to the attribute. Additionally, you can implement frame-by-frame layout effects by changing the values of animatable properties in the per-frame callback function.
4
5- Animatable attribute: If an attribute method is called before the **animation** attribute, and changing the value of this attribute can make the animation effect specified by the **animation** attribute take effect, then this attribute is called animatable attribute. For example, **height**, **width**, **backgroundColor**, **translate**, and **fontSize** (of the **Text** component) are all animatable attributes.
6
7- Non-animatable attribute: If an attribute method is called before the **animation** attribute, and changing the value of this attribute cannot make the animation effect specified by the **animation** attribute take effect, then this attribute is called non-animatable attribute. For example, the **points** attribute of the **Polyline** component is a non-animatable attribute.
8
9>  **NOTE**
10>
11>  This decorator is supported since API version 10. Updates will be marked with a superscript to indicate their earliest API version.
12>
13> This decorator can be used in atomic services since API version 11.
14
15## Rules of Use
16
17
18### Syntax
19
20
21```ts
22@AnimatableExtend(UIComponentName) function functionName(value: typeName) {
23  .propertyName(value)
24}
25```
26
27- \@AnimatableExtend can be defined only globally.
28- The parameter of the \@AnimatableExtend decorated function must be of the number type or a custom type that implements the **AnimatableArithmetic\<T\>** API.
29- In the \@AnimatableExtend decorated function body, only the attribute methods of the component specified in brackets immediately following \@AnimatableExtend can be called.
30
31### Available APIs
32The **AnimatableArithmetic** API defines the animation operation rules for non-number data types. To animate non-number data (such as arrays, structs, and colors), implement the addition, subtraction, multiplication, and equality judgment functions in the **AnimatableArithmetic\<T\>** API.
33In this way, the data can be involved in an interpolation operation of the animation and identify whether the data changes, that is, the non-number data is defined as the types that implement the **AnimatableArithmetic\<T\>** API.
34| Name| Input Parameter Type| Return Value Type| Description |
35| -------- | -------- |-------- |-------- |
36| plus | AnimatableArithmetic\<T\> | AnimatableArithmetic\<T\> | Defines the addition rule of the data type.|
37| subtract | AnimatableArithmetic\<T\> | AnimatableArithmetic\<T\> | Defines the subtraction rule of the data type.|
38| multiply | number | AnimatableArithmetic\<T\> | Defines the multiplication rule of the data type.|
39| equals | AnimatableArithmetic\<T\> | boolean | Defines the equality judgment rule of the data type.|
40
41## Example
42
43The following example implements the frame-by-frame layout effects by changing the width of the **Text** component.
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
74The following example implements a polyline animation effect.
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  plus(rhs: Point): Point {
87    return new Point(this.x + rhs.x, this.y + rhs.y)
88  }
89  subtract(rhs: Point): Point {
90    return new Point(this.x - rhs.x, this.y - rhs.y)
91  }
92  multiply(scale: number): Point {
93    return new Point(this.x * scale, this.y * scale)
94  }
95  equals(rhs: Point): boolean {
96    return this.x === rhs.x && this.y === rhs.y
97  }
98}
99
100// PointVector implements the AnimatableArithmetic<T> API.
101class PointVector extends Array<Point> implements AnimatableArithmetic<PointVector> {
102  constructor(value: Array<Point>) {
103    super();
104    value.forEach(p => this.push(p))
105  }
106  plus(rhs: PointVector): PointVector {
107    let result = new PointVector([])
108    const len = Math.min(this.length, rhs.length)
109    for (let i = 0; i < len; i++) {
110      result.push((this as Array<Point>)[i].plus((rhs as Array<Point>)[i]))
111    }
112    return result
113  }
114  subtract(rhs: PointVector): PointVector {
115    let result = new PointVector([])
116    const len = Math.min(this.length, rhs.length)
117    for (let i = 0; i < len; i++) {
118      result.push((this as Array<Point>)[i].subtract((rhs as Array<Point>)[i]))
119    }
120    return result
121  }
122  multiply(scale: number): PointVector {
123    let result = new PointVector([])
124    for (let i = 0; i < this.length; i++) {
125      result.push((this as Array<Point>)[i].multiply(scale))
126    }
127    return result
128  }
129  equals(rhs: PointVector): boolean {
130    if (this.length != rhs.length) {
131      return false
132    }
133    for (let i = 0; i < this.length; i++) {
134      if (!(this as Array<Point>)[i].equals((rhs as Array<Point>)[i])) {
135        return false
136      }
137    }
138    return true
139  }
140  get(): Array<Object[]> {
141    let result: Array<Object[]> = []
142    this.forEach(p => result.push([p.x, p.y]))
143    return result
144  }
145}
146
147@AnimatableExtend(Polyline) function animatablePoints(points: PointVector) {
148  .points(points.get())
149}
150
151@Entry
152@Component
153struct AnimatablePropertyExample {
154  @State points: PointVector = new PointVector([
155    new Point(50, Math.random() * 200),
156    new Point(100, Math.random() * 200),
157    new Point(150, Math.random() * 200),
158    new Point(200, Math.random() * 200),
159    new Point(250, Math.random() * 200),
160  ])
161  build() {
162    Column() {
163      Polyline()
164        .animatablePoints(this.points)
165        .animation({duration: 1000, curve: Curve.Ease}) // Set animation parameters.
166        .size({height:220, width:300})
167        .fill(Color.Green)
168        .stroke(Color.Red)
169        .backgroundColor('#eeaacc')
170      Button("Play")
171        .onClick(() => {
172          // points is a data type that implements the animation protocol. During the animation, points can be changed from the previous PointVector data to the new one based on the defined operation rules and animation parameters to generate the PointVector data of each frame and then generate an animation.
173          this.points = new PointVector([
174            new Point(50, Math.random() * 200),
175            new Point(100, Math.random() * 200),
176            new Point(150, Math.random() * 200),
177            new Point(200, Math.random() * 200),
178            new Point(250, Math.random() * 200),
179          ])
180        })
181    }.width("100%")
182    .padding(10)
183  }
184}
185```
186![image](figures/animatable-points.gif)
187