@AnimatableExtend 装饰器:定义可动画属性
@AnimatableExtend
装饰器用于自定义可动画的属性方法 在这个属性方法中修改组件不可动画的属性。在动画执行过程时,通过逐帧回调函数修改不可动画属性值,让不可动画属性也能实现动画效果。 也可通过逐帧回调函数每帧修改可动画属性的值,实现逐帧布局的效果。
可动画属性:如果一个属性方法在 animation 属性前调用,改变这个属性的值可以生效 animation 属性的动画效果,这个属性称为可动画属性。比如 height、width、backgroundColor、translate 属性,Text 组件的 fontSize 属性等。
不可动画属性:如果一个属性方法在 animation 属性前调用,改变这个属性的值不能生效 animation 属性的动画效果,这个属性称为不可动画属性。比如 Polyline 组件的 points 属性等。
装饰器使用说明
语法
bash
@AnimatableExtend(UIComponentName) function functionName(value: typeName) {
.propertyName(value)
}
@AnimatableExtend
仅支持定义在全局,不支持在组件内部定义@AnimatableExtend
定义的函数参数类型必须为 number 类型或者实现AnimtableArithmetic<T>
接口的自定义类型。@AnimatableExtend
定义的函数体内只能调用@AnimatableExtend
括号内组件的属性方法
AnimtableArithmetic<T>
接口说明
对复杂数据类型做动画,需要实现 AnimtableArithmetic<T>
接口中加法、减法、乘法和判断相等函数。
- plus
bash
1. 入参类型: `AnimtableArithmetic<T>`;
2. 返回值类型: `AnimtableArithmetic<T>`;
3. 加法函数
- subtract
bash
1. 入参类型: `AnimtableArithmetic<T>`
2. 返回值类型: `AnimtableArithmetic<T>`
3. 减法函数
- multiply
bash
1. 入参类型: number
2. 返回值类型: `AnimtableArithmetic<T>`
3. 乘法函数
- equals
bash
1. 入参类型: `AnimtableArithmetic<T>`
2. 返回值类型: boolean
3. 相等判断函数
使用场景
以下示例通过改变 Text 组件宽度实现逐帧布局的效果。
bash
@AnimatableExtend(Text)
function animatableWidth(width: number) {
.width(width)
}
@Entry
@Component
struct AnimatablePropertyExample {
@State textWidth: number = 80;
build() {
Column() {
Text("AnimatableProperty")
.animatableWidth(this.textWidth)
.animation({ duration: 2000, curve: Curve.Ease })
Button("Play")
.onClick(() => {
this.textWidth = this.textWidth == 80 ? 160 : 80;
})
}.width("100%")
.padding(10)
}
}
一下示例实现折现的动画效果
bash
class Point {
x: number
y: number
constructor(x: number, y: number) {
this.x = x
this.y = y
}
plus(rhs: Point): Point {
return new Point(this.x + rhs.x, this.y + rhs.y)
}
subtract(rhs: Point): Point {
return new Point(this.x - rhs.x, this.y - rhs.y)
}
multiply(scale: number): Point {
return new Point(this.x * scale, this.y * scale)
}
equals(rhs: Point): boolean {
return this.x === rhs.x && this.y === rhs.y
}
}
class PointVector extends Array<Point> implements AnimatableArithmetic<PointVector> {
constructor(value: Array<Point>) {
super();
value.forEach(p => this.push(p))
}
plus(rhs: PointVector): PointVector {
let result = new PointVector([])
const len = Math.min(this.length, rhs.length)
for (let i = 0; i < len; i++) {
result.push((this as Array<Point>)[i].plus((rhs as Array<Point>)[i]))
}
return result
}
subtract(rhs: PointVector): PointVector {
let result = new PointVector([])
const len = Math.min(this.length, rhs.length)
for (let i = 0; i < len; i++) {
result.push((this as Array<Point>)[i].subtract((rhs as Array<Point>)[i]))
}
return result
}
multiply(scale: number): PointVector {
let result = new PointVector([])
for (let i = 0; i < this.length; i++) {
result.push((this as Array<Point>)[i].multiply(scale))
}
return result
}
equals(rhs: PointVector): boolean {
if (this.length != rhs.length) {
return false
}
for (let i = 0; i < this.length; i++) {
if (!(this as Array<Point>)[i].equals((rhs as Array<Point>)[i])) {
return false
}
}
return true
}
get(): Array<Object[]> {
let result: Array<Object[]> = []
this.forEach(p => result.push([p.x, p.y]))
return result
}
}
@AnimatableExtend(Polyline) function animatablePoints(points: PointVector) {
.points(points.get())
}
@Entry
@Component
struct AnimatablePropertyExample {
@State points: PointVector = new PointVector([
new Point(50, Math.random() * 200),
new Point(100, Math.random() * 200),
new Point(150, Math.random() * 200),
new Point(200, Math.random() * 200),
new Point(250, Math.random() * 200),
])
build() {
Column() {
Polyline()
.animatablePoints(this.points)
.animation({duration: 1000, curve: Curve.Ease})
.size({height:220, width:300})
.fill(Color.Green)
.stroke(Color.Red)
.backgroundColor('#eeaacc')
Button("Play")
.onClick(() => {
this.points = new PointVector([
new Point(50, Math.random() * 200),
new Point(100, Math.random() * 200),
new Point(150, Math.random() * 200),
new Point(200, Math.random() * 200),
new Point(250, Math.random() * 200),
])
})
}.width("100%")
.padding(10)
}
}