写点什么

HarmonyOS:@AnimatableExtend 装饰器自学指南

作者:李游Leo
  • 2025-03-25
    北京
  • 本文字数:3206 字

    阅读完需:约 11 分钟

HarmonyOS:@AnimatableExtend 装饰器自学指南

在最近的项目开发中,我遇到了需要实现复杂动画效果的需求。在探索解决方案的过程中,我发现了 ​​@AnimatableExtend​​​ 装饰器,它为实现动画效果提供了一种非常灵活且强大的方式。然而,在学习这个装饰器的过程中,我发现相关的资料并不是特别丰富,而且很多资料都缺乏系统性的讲解。因此,我决定写这篇博客,将自己的学习经验和理解分享出来,希望能帮助更多的开发者快速掌握 ​​@AnimatableExtend​​ 装饰器的使用。

1. ​​@AnimatableExtend​​ 装饰器概述

​@AnimatableExtend​​ 装饰器从 API Version 10 开始支持,为动画效果的实现提供了一种便捷的方式。从 API version 11 开始,它支持在元服务中使用。这个装饰器允许我们自定义动画属性,使得我们可以对不同类型的数据进行动画处理。

1.1 装饰器使用规则

  • 定义位置:​​@AnimatableExtend​​ 仅支持定义在全局,不支持在组件内部定义。

  • 参数类型:​​@AnimatableExtend​​​ 定义的函数参数类型必须为 ​​number​​​ 类型或者实现 ​​AnimatableArithmetic<T>​​ 接口的自定义类型。

  • 函数体限制:​​@AnimatableExtend​​​ 定义的函数体内只能调用 ​​@AnimatableExtend​​ 括号内组件的属性方法。

1.2 ​​AnimatableArithmetic<T>​​ 接口说明

​AnimatableArithmetic<T>​​​ 接口定义了非 ​​number​​​ 数据类型的动画运算规则。对于非 ​​number​​ 类型的数据(如数组、结构体、颜色等)做动画,需要实现该接口中的加法、减法、乘法和判断相等函数,使得该数据能参与动画的插值运算和识别该数据是否发生改变。


2. 使用场景示例

2.1 改变 Text 组件宽度实现逐帧布局效果

下面的示例通过改变 ​​Text​​ 组件的宽度实现逐帧布局的效果。


@AnimatableExtend(Text)function animatableWidth(width: number) {  .width(width)}
@Entry@Componentstruct AnimatablePropertyExample { @State textWidth: number = 100;
build() { Column() { Text("AnimatableProperty") .animatableWidth(this.textWidth) .animation({ duration: 3000, curve: Curve.EaseInOut }) Button("Play") .onClick(() => { this.textWidth = this.textWidth == 100 ? 200 : 100; }) }.width("100%") .padding(20) }}
复制代码


在这个示例中,我们定义了一个 ​​animatableWidth​​​ 函数,通过 ​​@AnimatableExtend​​​ 装饰器将其应用到 ​​Text​​​ 组件上。点击按钮时,​​textWidth​​ 的值会在 100 和 200 之间切换,从而触发动画效果。

2.2 实现折线的动画效果

为了实现折线的动画效果,我们需要定义一个自定义类型 ​​Point​​​ 和 ​​PointVector​​​,并让 ​​PointVector​​​ 实现 ​​AnimatableArithmetic<T>​​ 接口。


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 }}
// PointVector实现了AnimatableArithmetic<T>接口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@Componentstruct AnimatablePropertyExample { @State points: PointVector = new PointVector([ new Point(30, Math.random() * 250), new Point(80, Math.random() * 250), new Point(130, Math.random() * 250), new Point(180, Math.random() * 250), new Point(230, Math.random() * 250), ])
build() { Column() { Polyline() .animatablePoints(this.points) .animation({ duration: 1500, curve: Curve.EaseOut })// 设置动画参数 .size({ height: 250, width: 350 }) .fill(Color.Blue) .stroke(Color.Yellow) .backgroundColor('#aaccff') Button("Play") .onClick(() => { // points是实现了可动画协议的数据类型,points在动画过程中可按照定义的运算规则、动画参数从之前的PointVector变为新的PointVector数据,产生每一帧的PointVector数据,进而产生动画 this.points = new PointVector([ new Point(30, Math.random() * 250), new Point(80, Math.random() * 250), new Point(130, Math.random() * 250), new Point(180, Math.random() * 250), new Point(230, Math.random() * 250), ]) }) }.width("100%") .padding(20) }}
复制代码



在这个示例中,我们定义了 ​​Point​​​ 类表示二维平面上的点,​​PointVector​​​ 类表示点的数组。通过实现 ​​AnimatableArithmetic<T>​​​ 接口,我们为 ​​PointVector​​​ 定义了加法、减法、乘法和相等判断规则。然后,我们使用 ​​@AnimatableExtend​​​ 装饰器将 ​​animatablePoints​​​ 函数应用到 ​​Polyline​​ 组件上,点击按钮时,折线的顶点位置会随机变化,从而实现动画效果。

3. 总结

​@AnimatableExtend​​​ 装饰器为实现复杂动画效果提供了一种强大而灵活的方式。通过自定义动画属性和实现 ​​AnimatableArithmetic<T>​​​ 接口,我们可以对不同类型的数据进行动画处理。在实际开发中,我们可以根据具体需求灵活运用这个装饰器,实现各种炫酷的动画效果。希望这篇博客能帮助你快速掌握 ​​@AnimatableExtend​​ 装饰器的使用,让你的应用更加生动有趣。


通过以上的学习和实践,你可以逐步掌握 ​​@AnimatableExtend​​ 装饰器的使用方法,并且在实际项目中灵活运用,为用户带来更加丰富的动画体验。

发布于: 刚刚阅读数: 5
用户头像

李游Leo

关注

全栈开发工程师、全栈讲师、华为HDE 2022-07-14 加入

原百度、时趣互动、乐视高级前端(软件)开发工程师。后在北京一所当地大学任教,主要职务是教学主任,也为网易云课堂微专业的前端课程负责人。

评论

发布
暂无评论
HarmonyOS:@AnimatableExtend 装饰器自学指南_HarmonyOS_李游Leo_InfoQ写作社区