【HarmonyOS开发小实践】ArkUI 动画系统属性动画、显式动画与路径动画
ArkUI 动画系统属性动画、显式动画与路径动画
动画让界面"活"起来
静态界面能用,但缺了动画的过渡会显得生硬。ArkUI 提供了从简单到复杂的动画能力:属性动画、显式动画、路径动画,以及组件内置动画。这篇把常用动画类型讲清楚。
属性动画:给属性加动画
属性动画是最简单的动画方式——某个属性值变化时,自动生成过渡动画,而不是瞬间跳变。
@Entry
@Component
struct AttributeAnimationDemo {
@State size: number = 100;
build() {
Column({ space: 20 }) {
// 一个会变大变小的方块
Text('')
.width(this.size)
.height(this.size)
.backgroundColor('#FF6B35')
.animation({ // 属性动画
duration: 300, // 时长 300ms
curve: Curve.EaseInOut // 缓动曲线
})
Button('变大')
.onClick(() => {
this.size = 200; // 改属性,动画自动过渡
})
Button('变小')
.onClick(() => {
this.size = 100;
})
}
}
}
.animation() 给这个组件的属性变化统一加动画。改 size 时,宽高不再是瞬间跳到 200,而是 300ms 内平滑过渡。
多属性组合动画
.animation() 一次只管一个组件的动画参数,但可以给多个属性加 animation,或指定动画哪些属性:
Text('')
.width(this.size)
.height(this.size)
.backgroundColor(this.color)
.borderRadius(this.radius)
.animation({ duration: 300, curve: Curve.EaseInOut }) // 所有动画属性统一动画
上面 size、color、radius 变化都会过渡。如果想对不同属性用不同动画,可以分别设置。
缓动曲线 Curve
缓动曲线决定了动画速度的变化方式,直接影响动画手感:
| 曲线 | 特点 | 适用 |
|---|---|---|
Curve.Linear | 匀速 | 进度条、循环动画 |
Curve.EaseIn | 慢进快出 | 元素消失、退出 |
Curve.EaseOut | 快进慢出 | 元素出现、进入 |
Curve.EaseInOut | 慢进慢出 | 大多数过渡 |
Curve.Friction | 摩擦减速 | 模拟物理惯性 |
App 里的常规过渡用 EaseInOut,进入动画用 EaseOut(快进慢出更自然),退出用 EaseIn。
显式动画:更精细的控制
属性动画是"改属性自动动",显式动画是"主动调 API 触发动画"。显式动画能控制更多细节:
@Entry
@Component
struct ExplicitAnimationDemo {
@State width: number = 100;
build() {
Column({ space: 20 }) {
Text('')
.width(this.width)
.height(100)
.backgroundColor('#4A90E2')
Button('显式动画展开')
.onClick(() => {
// 显式动画:包住要动画的状态修改
this.getUIContext().animateTo({
duration: 500,
curve: Curve.EaseInOut,
iterations: 1, // 播放次数
playMode: PlayMode.Normal
}, () => {
// 闭包里改状态,触发动画
this.width = 300;
});
})
}
}
}
animateTo 的第一个参数是动画配置,第二个参数是回调,回调里的状态修改会被动画化。显式动画的优点:
- 能控制
iterations(次数)、playMode(播放模式) - 能把多个状态修改放在一个闭包里,统一做动画
显式动画 vs 属性动画
属性动画:改属性 → 自动动(简单,适合固定动画)
显式动画:调 API → 闭包改状态 → 动画(灵活,适合复杂控制)
选型:
普通 hover、尺寸变化 → 属性动画 .animation()
需要控制次数/方向/多状态联动 → 显式动画 animateTo()
路径动画:沿轨迹运动
属性动画只能做值的变化(大小、颜色、透明度),路径动画让元素沿指定的轨迹运动:
@Entry
@Component
struct PathAnimationDemo {
@State offsetX: number = 0;
@State offsetY: number = 0;
build() {
Stack() {
// 沿圆形轨迹运动的小球
Circle()
.width(30)
.height(30)
.fill('#FF6B35')
.offset({ x: this.offsetX, y: this.offsetY })
.animation({ duration: 2000, curve: Curve.Linear })
}
.onClick(() => {
// 圆周运动:通过三角函数更新位置
let angle = 0;
let timer = setInterval(() => {
angle += 0.1;
this.offsetX = 150 * Math.cos(angle); // x = r·cos(θ)
this.offsetY = 150 * Math.sin(angle); // y = r·sin(θ)
if (angle >= Math.PI * 2) {
clearInterval(timer);
}
}, 50);
})
}
}
路径动画本质是"按时间轴手动更新位置,配合属性动画做平滑过渡"。复杂路径(贝塞尔曲线、抛物线)都是这个思路——用数学公式算出每一帧的位置。
转场动画动画的完整案例
用属性动画 + 缓动曲线做一个"侧滑菜单":
@Entry
@Component
struct SlideMenuDemo {
@State isOpen: boolean = false;
@State menuOffset: number = -250; // 菜单默认藏在左侧
build() {
Stack({ alignContent: Alignment.Start }) {
// 主内容
Column() {
Text('主内容区')
Button('打开菜单')
.onClick(() => {
this.isOpen = !this.isOpen;
this.menuOffset = this.isOpen ? 0 : -250;
})
}
.width('100%')
.height('100%')
// 侧滑菜单
Column() {
Text('菜单项 1')
Text('菜单项 2')
Text('菜单项 3')
}
.width(250)
.height('100%')
.backgroundColor('#333333')
.offset({ x: this.menuOffset })
.animation({ duration: 300, curve: Curve.EaseInOut }) // 平滑滑入滑出
}
}
}
菜单的 offset 在 -250 和 0 之间切换,.animation() 让它平滑滑动,300ms 的 EaseInOut 手感刚好。
动画的几条经验
-
时长别太长。界面过渡动画 200-400ms 合适,超过 500ms 会让人感觉"卡"。加载动画可以循环,但转场要快。
-
缓动曲线比时长更影响手感。同样的 300ms,
Linear生硬,EaseInOut自然。默认EaseInOut是安全选择。 -
能属性动画就别显式动画。属性动画代码更简洁,只有需要控制次数、方向、多状态联动时才用
animateTo。 -
避免动画里做重计算。动画回调(尤其是路径动画的 timer)里别做昂贵操作,否则掉帧。
-
列表项动画要节制。长列表里每项都做复杂动画会影响滚动性能,列表动画优先用组件内置的轻量动效。
更多推荐
所有评论(0)