HarmonyOS6 PC 动画开发实战:五种 Curve 曲线类型,到底该选哪个?
做 HarmonyOS6 PC 端开发的时候,动画这个东西真的是"一看就会,一做就废"。
很多开发者(包括我自己)写动画的时候最常干的事情就是:duration 给个 300,curve 用个默认的,然后就不管了。直到有一天产品经理过来说"这个动画怎么这么生硬",才意识到——动画曲线才是灵魂。
今天咱们就把 HarmonyOS ArkUI 里的 Curve 枚举彻底搞明白。不搞虚的,直接上对比,让你看完就知道什么场景用什么曲线。

先搞清楚 animateTo() 这个全局函数
HarmonyOS 的 ArkUI 框架提供了一个全局动画函数 animateTo(),它的签名很简单:
animateTo(
value: AnimateParam,
closure: () => void
)
AnimateParam 里面有几个关键参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| duration | number | 动画时长,单位毫秒 |
| curve | Curve | string | 动画曲线,也就是今天的主角 |
| delay | number | 延迟执行时间 |
| iterations | number | 重复次数 |
| onFinish | () => void | 动画结束回调 |
closure 闭包里面放的就是你要改变的状态变量。ArkUI 会自动检测闭包里哪些 @State 变量发生了变化,然后对使用了这些变量的 UI 属性做动画插值。
这个设计坦白讲非常优雅——你只需要告诉框架"从 A 变到 B",中间的过渡帧它帮你算。
Curve 枚举全家福
ArkUI 内置了这些 Curve 值:
enum Curve {
Linear, // 匀速
Ease, // 默认缓动,类似 EaseInOut
EaseIn, // 慢入快出
EaseOut, // 快入慢出
EaseInOut, // 慢入慢出(两头慢中间快)
FastOutLinearIn, // 快速启动,匀速结束
FastOutSlowIn, // 快速启动,缓慢结束
LinearOutSlowIn, // 匀速启动,缓慢结束
Sharp, // 锐利曲线
Rhythm, // 节奏曲线
Smooth, // 平滑曲线
Friction, // 摩擦力曲线(模拟物理阻尼)
}
一共12种。说实话,日常开发用到的也就五六种,但搞清楚每一种的特性,关键时刻能救命。
实测对比:5种常用曲线
下面这段代码做了一个非常直观的对比实验——5个进度条,分别使用 Linear、EaseIn、EaseOut、EaseInOut、FastOutLinearIn 五种曲线,同时从 0% 跑到 90%,时长统一 1500ms。
@Entry
@Component
struct CurveCompareDemo {
@State positions: number[] = [0, 0, 0, 0, 0]
@State labels: string[] = ['Linear', 'EaseIn', 'EaseOut', 'EaseInOut', 'FastOutLinearIn']
build() {
Column() {
Text('曲线动画对比')
.fontSize(18).fontWeight(FontWeight.Bold).margin({ bottom: 8 })
Column() {
ForEach([0, 1, 2, 3, 4], (idx: number) => {
Row() {
Text(this.labels[idx])
.width(70).fontSize(11).fontColor('#999999')
Stack({ alignContent: Alignment.Start }) {
Column()
.width('100%').height(8)
.backgroundColor('#E0E0E0')
.borderRadius(4)
Column()
.width(`${this.positions[idx]}%`).height(8)
.backgroundColor(this.getColor(idx))
.borderRadius(4)
.animation({ duration: 1500 })
}
.layoutWeight(1)
}
.width('100%').height(36).alignItems(VerticalAlign.Center)
})
Row({ space: 10 }) {
Button('启动对比')
.onClick(() => {
this.startAnimation(0, Curve.Linear)
this.startAnimation(1, Curve.EaseIn)
this.startAnimation(2, Curve.EaseOut)
this.startAnimation(3, Curve.EaseInOut)
this.startAnimation(4, Curve.FastOutLinearIn)
})
Button('重置')
.onClick(() => {
for (let i = 0; i < 5; i++) { this.positions[i] = 0 }
})
}
.width('100%').justifyContent(FlexAlign.SpaceEvenly).margin({ top: 16 })
}
.width('100%').backgroundColor('#FFFFFF').borderRadius(12).padding(16)
}
.width('100%').height('100%').backgroundColor('#F5F6FA').padding(16)
}
getColor(index: number): string {
const colors = ['#FF6B6B', '#FFA500', '#FFD93D', '#6BCB77', '#4ECDC4']
return colors[index]
}
startAnimation(index: number, curve: Curve) {
animateTo({ duration: 1500, curve: curve }, () => {
this.positions[index] = 90
})
}
}

运行效果描述
点击"启动对比"后,5个进度条同时开始跑:
- Linear:匀速前进,速度恒定,看着很"机械"
- EaseIn:起步特别慢,后半段加速冲上去
- EaseOut:起步很快,快到终点时慢慢减速
- EaseInOut:起步慢 → 中间加速 → 到达时减速,最自然的运动感
- FastOutLinearIn:嗖的一下冲出去,然后匀速到达
这个对比实验虽然简单,但效果非常直观。你会发现 1500ms 的时长足够长,每条曲线的速度差异肉眼可辨。
每种曲线的数学特性和适用场景
Linear(线性)
数学上就是一条直线 y = x,没有加减速。
适用场景:进度条、倒计时、持续旋转动画。这类场景本身就不需要加减速,匀速反而更准确。
不适用场景:几乎所有位移类动画。一个按钮从左边飞到右边用 Linear 曲线,看着就像机器人推过去的。
EaseIn(缓入)
曲线特征是 y = x^n(n > 1),起点斜率接近 0,终点斜率最大。
适用场景:元素离场/退出。比如一个卡片飞出屏幕,用 EaseIn 会让它看起来像"蓄力然后加速离开",符合物理直觉。
踩坑经验:别用 EaseIn 做入场动画。一个元素从屏幕外飞进来,如果用 EaseIn,它会在屏幕边缘磨蹭半天,用户等着急。
EaseOut(缓出)
跟 EaseIn 反过来,起点斜率最大,终点逐渐减速至 0。
适用场景:元素入场。从屏幕外飞入的弹窗、滑入的抽屉菜单、展开的下拉面板——用 EaseOut 就对了。它给人一种"物体因为摩擦力而逐渐停下来"的感觉。
坦白讲,如果你不知道用什么曲线,EaseOut 是最安全的选择。大多数 Material Design 和 Apple HIG 推荐的交互动画默认就是 EaseOut 类型的曲线。
EaseInOut(缓入缓出)
两头慢中间快,像一个钟摆运动。
适用场景:状态切换类动画。比如开关按钮的滑动、Tab 切换的位移、展开/折叠面板。这类动画没有明确的"入场"和"离场"之分,用 EaseInOut 最自然。
在 HarmonyOS6 PC 端,很多系统级动画就是 EaseInOut 曲线,比如窗口最小化/恢复。保持跟系统一致的动画风格,用户体验会更统一。
FastOutLinearIn(快出匀入)
起步很快,后半段匀速。这是个比较特殊的曲线。
适用场景:需要快速响应的位移动画,比如快速展开的菜单、工具栏的出现。它比 EaseOut 更"干脆",没有明显的减速过程。
不太推荐用在需要优雅感的场景中,因为匀速结束的部分会显得有点"突然就停了"。
另外几种曲线简评
上面5种是最常用的,剩下几种也简单聊聊:
FastOutSlowIn
快速启动 + 缓慢结束。跟 EaseOut 类似但减速段更长,适合需要"慢慢到位"的场景,比如大尺寸面板的展开。在 HarmonyOS6 PC 端的大屏幕上,面板展开动画用这个曲线特别合适,因为大屏用户对动画的"从容感"要求更高。
LinearOutSlowIn
匀速启动 + 缓慢结束。起步不会很突兀,结束很柔和。适合那些"不要太快但也不能太慢"的微妙场景。
Sharp
锐利曲线,过渡非常快。适合需要"一闪而过"效果的场景,比如消息气泡的消失。
Friction
模拟摩擦力阻尼的效果,会有轻微的回弹感。非常适合拖拽释放后的归位动画——你拖一个卡片松手后它弹回原位,用 Friction 曲线特别真实。
Smooth 和 Rhythm
这两个比较偏"感觉型",Smooth 更丝滑,Rhythm 有节奏感。实际项目中用的不多,但在一些品牌展示类页面中可以考虑。
animateTo() vs .animation() 到底用哪个?
这是很多 HarmonyOS6 PC 开发者会纠结的问题。上面的对比 Demo 里其实两种写法都有:
// 方式一:animateTo() 全局函数
animateTo({ duration: 1500, curve: Curve.Linear }, () => {
this.positions[0] = 90
})
// 方式二:.animation() 属性修饰器
Column()
.width(`${this.positions[idx]}%`)
.animation({ duration: 1500 })
区别在哪?
animateTo() 是命令式的——你在某个事件回调里主动调用,可以精确控制动画的触发时机、曲线、延迟,还能拿到 onFinish 回调。适合"点击按钮后执行一组动画"这种场景。
.animation() 是声明式的——你把它挂在组件上,当绑定的状态变量发生变化时自动触发动画。它更"懒",但也更简洁。适合"状态变了就自动过渡"的场景。
实际项目里我建议混合使用。比如上面的对比 Demo,进度条用了 .animation() 修饰器(因为宽度变化只需要简单的过渡),而触发动画用了 animateTo()(因为需要指定不同的曲线)。
有个细节:当两者同时存在时,animateTo() 的 curve 参数会覆盖 .animation() 的 curve。所以你在 .animation() 上设的 curve,可能会在 animateTo() 调用时失效。这个坑我踩过,记录一下。
PC 端动画的特殊考量
做 HarmonyOS6 PC 端开发和手机端有个很大的区别:屏幕大,动画的位移距离更长。
一个卡片从屏幕左侧飞到右侧,在手机上可能就 300px 的距离,在 PC 端可能是 800px 甚至更多。同样的 duration 和 curve,在不同距离下的视觉感受完全不同。
我的经验法则:
- 短距离位移(< 100px):300-400ms,EaseOut
- 中距离位移(100-300px):400-600ms,EaseOut 或 EaseInOut
- 长距离位移(> 300px):600-800ms,EaseInOut 或 FastOutSlowIn
- 缩放/透明度变化:不受距离影响,保持 200-400ms 即可
PC 端用户对动画的容忍度比手机端高一些(可能因为大屏本身就有"大气"的感觉),但动画太慢一样会被吐槽。建议在真实设备上反复调试,别在模拟器上看着差不多就完事了。
一个实用的曲线选择决策树

最后分享一个我自己用的决策流程:
要做动画 → 是什么类型?
├── 进度/持续旋转 → Linear
├── 元素入场(从外到内)→ EaseOut
├── 元素离场(从内到外)→ EaseIn
├── 状态切换/原地变形 → EaseInOut
├── 需要快速响应 → FastOutLinearIn 或 FastOutSlowIn
├── 拖拽回弹 → Friction
└── 不确定 → EaseOut(万能选择)
保存这个决策树,下次写动画的时候就不用纠结了。
小结
动画曲线这个东西,说到底是让 UI 的变化"像真实世界里的运动"。真实世界里,物体不会瞬间启动也不会瞬间停止——它们有加速和减速的过程。Curve 枚举就是在模拟这个过程。
把上面那个对比 Demo 跑一遍,盯着 5 个进度条看十几遍,你对曲线的理解会比看任何文档都深刻。
更多推荐
所有评论(0)