HarmonyOS 动画与转场:从属性动画到关键帧


一、引言
动画是提升应用体验的关键因素。一个优秀的动画不仅能让界面更加生动,还能引导用户注意力、传达状态变化、增强操作反馈。HarmonyOS ArkUI 提供了强大的动画能力,包括隐式动画、显式动画、转场动画、关键帧动画、弹簧动画等。
本文将以一个霓虹暗色风格的动画演示页面为主线,深入讲解 ArkUI 动画体系的各个层次,通过大量代码示例帮助读者掌握动画开发的核心技能。
二、动画的分类
ArkUI 的动画体系可以从多个维度分类:
| 分类维度 | 类型 | 说明 |
|---|---|---|
| 触发方式 | 隐式动画 | 属性变化自动过渡 |
| 触发方式 | 显式动画 | 闭包内状态变化生效 |
| 作用对象 | 属性动画 | 对单个属性做动画 |
| 作用对象 | 转场动画 | 组件插入/移除过渡 |
| 控制方式 | 关键帧动画 | 多段关键帧控制 |
| 物理模型 | 弹簧动画 | 模拟物理弹性 |
2.1 隐式动画(animation)
隐式动画通过 animation 属性声明,当组件的属性(如位置、大小、颜色、透明度)发生变化时,自动以动画形式过渡:
Text('Hello')
.scale({ x: this.scale, y: this.scale })
.animation({
duration: 1000, // 动画时长(毫秒)
curve: Curve.EaseInOut, // 动画曲线
delay: 0, // 延迟时间
iterations: 1, // 迭代次数
playMode: PlayMode.Normal // 播放模式
})
2.2 显式动画(animateTo)
显式动画通过 animateTo 函数触发,在闭包内修改状态,框架自动为这些状态变化添加动画:
animateTo({ duration: 1000 }, () => {
this.scale = 1.5; // 闭包内的状态变化会带动画
});
2.3 隐式与显式的区别
| 特性 | 隐式动画 | 显式动画 |
|---|---|---|
| 声明位置 | 组件属性上 | 代码块中 |
| 触发方式 | 属性变化自动触发 | 调用 animateTo 触发 |
| 控制粒度 | 单个组件 | 闭包内所有变化 |
| 使用场景 | 单个属性动画 | 多个属性/组件联动 |
三、动画参数详解
3.1 动画时长
duration: 1000 // 单位毫秒,1000 = 1 秒
3.2 动画曲线
动画曲线(curve)控制动画的速度变化,ArkUI 提供多种内置曲线:
Curve.Linear // 匀速
Curve.Ease // 慢-快-慢
Curve.EaseIn // 加速
Curve.EaseOut // 减速
Curve.EaseInOut // 慢-快-慢(更平滑)
Curve.FastOutSlowIn // 快出慢入
Curve.SpringCurve // 弹簧曲线
3.3 播放模式
PlayMode.Normal // 正常播放
PlayMode.Reverse // 反向播放
PlayMode.Alternate // 交替播放(正-反-正)
PlayMode.AlternateReverse // 交替反向播放
3.4 迭代次数
iterations: 1 // 播放 1 次
iterations: 3 // 播放 3 次
iterations: -1 // 无限循环
四、实战代码:霓虹暗色动画页面
下面我们实现一个霓虹风格的动画演示页面,包含层叠动画区和动画 API 速查表。
4.1 定义数据结构
interface AnimRow {
param: string;
value: string;
}
代码说明:
AnimRow 接口描述动画 API 速查表中的一行数据,包含 API 名称和说明。
4.2 组件状态定义
@Entry
@Component
struct AnimationPage {
@State scale: number = 1;
@State rotate: number = 0;
@State opacity: number = 1;
@State playing: boolean = false;
@State animRows: AnimRow[] = [
{ param: 'animation', value: '隐式动画,属性变化自动过渡' },
{ param: 'animateTo', value: '显式动画,闭包内状态变化生效' },
{ param: 'transition', value: '组件插入/移除转场' },
{ param: 'keyframeAnimateTo', value: '关键帧动画,多段控制' },
{ param: 'springMotion', value: '弹簧物理动画' }
];
代码说明:
@State scale:缩放比例,控制方块大小。@State rotate:旋转角度,控制方块旋转。@State opacity:透明度,控制方块透明程度。@State playing:动画播放状态标志。@State animRows:动画 API 速查表数据。
4.3 动画控制方法
togglePlay(): void {
this.playing = !this.playing;
if (this.playing) {
animateTo({ duration: 1500, curve: Curve.EaseInOut, iterations: -1, playMode: PlayMode.Alternate }, () => {
this.scale = 1.6;
this.rotate = 360;
this.opacity = 0.4;
});
} else {
animateTo({ duration: 500, curve: Curve.EaseOut }, () => {
this.scale = 1;
this.rotate = 0;
this.opacity = 1;
});
}
}
代码说明:
togglePlay 方法是动画控制的核心:
-
切换状态:
this.playing = !this.playing切换播放/停止状态。 -
播放动画:调用
animateTo显式动画,参数说明:duration: 1500:动画时长 1.5 秒。curve: Curve.EaseInOut:缓入缓出曲线。iterations: -1:无限循环。playMode: PlayMode.Alternate:交替播放,动画会在正反两个方向来回切换,形成"呼吸"效果。
-
动画目标:闭包内同时修改三个属性:
this.scale = 1.6:方块放大到 1.6 倍。this.rotate = 360:方块旋转 360 度。this.opacity = 0.4:方块透明度降低到 40%。
-
停止动画:调用
animateTo将属性恢复初始值,duration: 500用较短时间快速复位。 -
关键理解:
animateTo闭包内的所有状态变化都会被包装成动画,这是与隐式动画(单属性)最大的区别。
4.4 构建 UI:层叠动画区
build() {
Scroll() {
Column({ space: 16 }) {
// 顶部霓虹标题
Column() {
Text('ANIMATION')
.fontSize(12)
.fontColor('#00F5FF')
.letterSpacing(6)
Text('动画与转场')
.fontSize(26)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
.margin({ top: 6 })
Text('属性动画 · 显式动画 · 关键帧')
.fontSize(12)
.fontColor('#00F5FF')
.margin({ top: 6 })
}
.width('100%')
.padding({ top: 48, bottom: 30 })
.backgroundColor('#0A0A0F')
// Stack 层叠动画区
Stack({ alignContent: Alignment.Center }) {
// 底层霓虹圆
Circle({ width: 180, height: 180 })
.fill('none')
.stroke('#00F5FF')
.strokeWidth(2)
.opacity(0.6)
.shadow({ radius: 30, color: '#8800F5FF' })
// 中层光环
Circle({ width: 120, height: 120 })
.fill('none')
.stroke('#FF2BD6')
.strokeWidth(1.5)
.opacity(0.5)
.shadow({ radius: 24, color: '#88FF2BD6' })
// 上层方块
Column() {
Text('NEON')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#00F5FF')
}
.width(80)
.height(80)
.justifyContent(FlexAlign.Center)
.backgroundColor('#111122')
.borderRadius(12)
.border({ width: 2, color: '#00F5FF' })
.shadow({ radius: 20, color: '#AA00F5FF' })
.scale({ x: this.scale, y: this.scale })
.rotate({ angle: this.rotate })
.opacity(this.opacity)
}
.width('100%')
.height(260)
.backgroundColor('#0A0A0F')
代码说明:
层叠动画区使用 Stack 布局,实现三层元素的叠加:
-
Stack 布局:
Stack({ alignContent: Alignment.Center })将所有子元素居中对齐堆叠,后面的元素覆盖在前面的元素上。 -
底层霓虹圆:
Circle组件绘制圆形,fill('none')表示不填充,stroke('#00F5FF')设置青色描边,shadow添加霓虹发光效果。 -
中层光环:粉色描边圆环,颜色与底层形成对比,增加层次感。
-
上层方块:这是动画作用的核心元素:
.scale({ x: this.scale, y: this.scale }):绑定缩放状态。.rotate({ angle: this.rotate }):绑定旋转状态。.opacity(this.opacity):绑定透明度状态。- 当
togglePlay修改这三个状态时,方块会同步进行缩放、旋转和透明度变化的组合动画。
-
霓虹风格:青色和粉色描边配合强发光阴影(
shadow的color带高透明度),在黑色背景上形成霓虹灯效果。
// 发光按钮
Button(this.playing ? '停止动画' : '播放动画')
.width('100%')
.height(50)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#0A0A0F')
.backgroundColor('#00F5FF')
.borderRadius(25)
.shadow({ radius: 24, color: '#AA00F5FF', offsetY: 0 })
.onClick(() => { this.togglePlay(); })
代码说明:
播放/停止按钮采用霓虹发光风格:
- 背景色为青色
#00F5FF,文字为深色,形成高对比。 .shadow({ radius: 24, color: '#AA00F5FF' })设置大范围发光阴影,让按钮看起来像霓虹灯管。- 按钮文字根据
playing状态动态切换。
// 动画参数暗色表格
Column() {
Text('动画 API 速查')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#00F5FF')
.alignSelf(ItemAlign.Start)
.margin({ bottom: 8 })
ForEach(this.animRows, (row: AnimRow) => {
Row({ space: 10 }) {
Text(row.param)
.fontSize(12)
.fontWeight(FontWeight.Bold)
.fontColor('#FF2BD6')
.width(130)
.fontFamily('monospace')
Text(row.value)
.fontSize(11)
.fontColor('#BBBBBB')
.layoutWeight(1)
}
.width('100%')
.padding({ top: 10, bottom: 10 })
.border({ width: { bottom: 1 }, color: '#1F1F2E' })
})
}
.width('100%')
.padding(16)
.backgroundColor('#111122')
.borderRadius(14)
.border({ width: 1, color: '#00F5FF44' })
代码说明:
动画 API 速查表采用暗色表格风格:
- 表头使用青色文字,与页面主色调一致。
- API 名称使用粉色等宽字体,突出技术感。
- 说明文字使用灰色。
- 行与行之间用深色边框分隔,整体融入暗色背景。
五、转场动画
5.1 组件转场(transition)
转场动画用于组件插入和移除时的过渡效果:
if (this.show) {
Column() {
Text('新内容')
}
.transition(
TransitionEffect.OPACITY
.combine(TransitionEffect.translate({ y: 100 }))
.animation({ duration: 300 })
)
}
代码说明:
TransitionEffect.OPACITY:透明度转场效果。.combine():组合多个转场效果。.translate({ y: 100 }):位移转场效果。- 当
show为 true 时组件淡入并上移,为 false 时淡出并下移。
5.2 页面转场
页面跳转时可以设置转场动画:
router.pushUrl({
url: 'pages/DetailPage',
animation: {
duration: 300,
curve: Curve.EaseInOut
}
});
六、关键帧动画
关键帧动画允许精确控制动画在不同时间点的状态:
keyframeAnimateTo({
duration: 2000,
iterations: 1,
keyframes: [
{ duration: 0.25, event: () => { this.x = 100; this.y = 0; } },
{ duration: 0.25, event: () => { this.x = 100; this.y = 100; } },
{ duration: 0.25, event: () => { this.x = 0; this.y = 100; } },
{ duration: 0.25, event: () => { this.x = 0; this.y = 0; } }
]
});
代码说明:
keyframeAnimateTo是显式关键帧动画 API。keyframes数组定义多个关键帧,每个关键帧包含duration(占动画总时长的比例)和event(该时刻的状态修改闭包)。- 上面的例子让元素沿正方形路径运动一圈。
七、弹簧动画
弹簧动画模拟物理弹簧的弹性效果,让动画更自然:
import { curves } from '@kit.ArkUI';
// 方式一:使用弹簧曲线
animateTo({
curve: curves.springMotion(0.5, 0.8)
}, () => {
this.scale = 1.5;
});
// 方式二:使用弹簧动画
this.scale = 1.5;
animateTo({
curve: curves.springCurve(0.4, 1.0)
}, () => {
this.scale = 1.0;
});
代码说明:
curves.springMotion(response, dampingFraction):弹簧运动曲线,response控制响应速度,dampingFraction控制阻尼。- 弹簧动画在到达目标值后会有回弹效果,模拟真实物理运动。
八、动画性能优化
8.1 使用 transform 属性
动画尽量使用 transform 相关属性(scale、rotate、translate),这些属性不触发布局重排,性能更好:
// 推荐:transform 动画
.translate({ x: this.offsetX })
.scale({ x: this.scale })
// 不推荐:修改布局属性
.width(this.newWidth)
8.2 避免动画大量组件
对大量组件同时做动画会消耗大量性能,应尽量只对必要组件做动画。
8.3 合理设置动画时长
动画时长过短会显得生硬,过长会让用户等待。一般交互反馈 200-300ms,转场 300-500ms,强调动画 500-1000ms。
九、常见问题
9.1 动画不生效
原因:可能是隐式动画的 animation 属性声明位置错误,或状态修改方式不正确。
解决:确认 animation 声明在需要动画的属性之后;使用 animateTo 时确认状态在闭包内修改。
9.2 动画循环不停止
原因:iterations: -1 表示无限循环。
解决:在需要停止时重新设置状态并调用 animateTo 复位。
9.3 动画卡顿
原因:动画了过多组件或使用非 transform 属性。
解决:优化动画对象,使用 transform 属性。
十、总结
本文深入讲解了 HarmonyOS 动画与转场技术,通过一个霓虹暗色风格的演示页面实战演示了显式动画、属性动画等核心能力。
核心要点回顾:
- 隐式动画(
animation)用于单属性自动过渡。 - 显式动画(
animateTo)用于闭包内多属性联动。 - 动画参数包括时长、曲线、播放模式、迭代次数。
- 转场动画(
transition)用于组件插入/移除。 - 关键帧动画(
keyframeAnimateTo)精确控制时间点状态。 - 弹簧动画模拟物理弹性,更自然。
- 性能优化:使用 transform 属性,避免动画大量组件。
动画是提升用户体验的重要工具,掌握它能让我们构建出更生动、更有质感的应用。下一篇我们将讲解 HarmonyOS 多媒体开发。
更多推荐



所有评论(0)