动画不就是加个 duration 吗?那为啥你的界面像“瞬移”,别人却丝滑到想鼓掌?

🏆本文收录于「滚雪球学SpringBoot」专栏(全网一个名),手把手带你零基础入门Spring Boot,从入门到就业,助你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
环境说明:Windows 10 + IntelliJ IDEA 2021.3.2 + Jdk 1.8
🤨🎬 摘要
你有没有遇到过这种“看起来只差一丢丢,但体验就差十万八千里”的场景:按钮点下去啪一下就变了、弹窗像“瞬移”、拖拽卡得像在推冰箱……然后你心里默默冒出一句:“这动画……是不是在跟我赌气啊?😤”
行,今天咱就把鸿蒙 ArkUI 的动画与过渡效果狠狠干明白——不玄学、不念经、能直接抄代码跑起来那种😄✨
🧭📌 目录(先把“丝滑”拆开讲🧩)
- 😄📝 前言:动画不是“装饰”,是体验的“情绪表达”
- 🧠⚡ animateTo / Animation API:你到底该用谁?
- 🫥➡️ 显式 vs 隐式动画:别把“自动动画”当成“自动背锅”
- 🧩🌈 组件过渡 transition:出现/消失也要讲礼貌
- 🤌🕺 手势动画:跟手、回弹、阻尼,像“有弹性的世界”
- 🧯🧨 常见翻车现场:我替你踩过了😭
- 🎯✅ 总结:把动画做成“系统级手感”的小抄
- ❓🤝 小确认:你现在项目是偏 ArkUI 声明式(ArkTS)吗?目标系统大概是 HarmonyOS 5.x 还是 NEXT/更高 API?
😄📝 前言:动画不是“装饰”,是体验的“情绪表达”💬
很多人一提动画就说:“锦上添花嘛,先把功能做完再说。”
但我想反问一句(带点小情绪哈😏):你愿意用一个‘每次点击都像抽搐’的 App 吗?
动画的价值其实很“现实”:
- ✅ 告诉用户发生了什么(状态变化可感知)
- ✅ 引导注意力(哪里是关键变化)
- ✅ 掩盖延迟(加载、跳转更自然)
- ✅ 让界面像“活的”(手感、气质、品质感)
一句话:没有动画不一定差,但乱用动画一定翻车😅
🧠⚡ animateTo / Animation API:你到底该用谁?🧐
在鸿蒙 ArkUI(声明式 UI)里,动画的核心思路其实很统一:
用状态驱动 UI,然后用动画“优雅地”过渡状态变化。 🧠✨
你会常用两类方式:
- 🎯 animateTo(显式动画):你明确告诉系统“接下来这段状态变化要动起来”
- 🎛️ Animation API / 组件级动画配置(更偏隐式/声明式):让某些属性变化“自动带动画”
✅ 选型直觉(我个人的“偷懒但靠谱”经验)
- 需要“这一段状态变化”统一进动画(比如同时改宽高/透明度/偏移) → 用 animateTo
- 需要“某个组件属性变化就自然动”(比如颜色、scale 小动效) → 用 隐式动画/Animation 配置
- 需要组件出现/消失带动效(插入/移除) → 用 transition
- 跟手拖拽、滑动、回弹 → 手势 + 动画(先跟手,再回弹)
🫥➡️ 显式 vs 隐式动画:别把“自动动画”当成“自动背锅”😆
🎯 1)显式动画:animateTo(最“可控”)
场景:点赞按钮,点一下“弹一下”(经典但好用🤣)
import { Curve } from '@kit.ArkUI';
@Entry
@Component
struct LikeButtonDemo {
@State liked: boolean = false
@State scaleV: number = 1
private bounce() {
// 先放大,再回到 1(两段动画更像“弹一下”)
animateTo({ duration: 140, curve: Curve.EaseOut }, () => {
this.scaleV = 1.18
})
animateTo({ duration: 180, curve: Curve.EaseInOut }, () => {
this.scaleV = 1
})
}
build() {
Column({ space: 16 }) {
Text(this.liked ? '❤️ 已点赞' : '🤍 点个赞呗')
.fontSize(20)
Button(this.liked ? '取消' : '点赞')
.scale({ x: this.scaleV, y: this.scaleV })
.onClick(() => {
this.liked = !this.liked
this.bounce()
})
}
.padding(24)
}
}
显式动画的优点:你说动就动、什么时候动、怎么动,你说了算😎
缺点:写多了会啰嗦(但至少不玄学)
🫥 2)隐式动画:状态一变,属性自己“滑过去”
隐式动画更像:你告诉组件“我希望你变化时自带动效”,然后你只管改状态就行。
场景:一个开关卡片展开/收起(高度、透明度一起过渡)
import { Curve } from '@kit.ArkUI';
@Entry
@Component
struct ExpandCardDemo {
@State expanded: boolean = false
build() {
Column({ space: 12 }) {
Row() {
Text('🧩 详情卡片').fontSize(20).layoutWeight(1)
Button(this.expanded ? '收起' : '展开')
.onClick(() => this.expanded = !this.expanded)
}
// 根据 expanded 改 UI(这才是声明式的灵魂)
Column() {
Text('这里是一些内容:')
Text('- 动画要自然')
Text('- 过渡要连贯')
Text('- 手感要像系统自带那样😌')
}
.padding(12)
.opacity(this.expanded ? 1 : 0)
.height(this.expanded ? 120 : 0)
.clip(true)
// 👇 关键:让属性变化带动画(隐式)
.animation({ duration: 220, curve: Curve.EaseInOut })
}
.padding(24)
}
}
小吐槽:隐式动画就像“自动挡”,舒服是舒服,但你得知道它什么时候会“自己换挡”😅
建议:别在复杂列表里对一堆 item 同时用隐式动画,容易抖、也费性能(后面我会骂它😤)
🧩🌈 组件过渡 transition:出现/消失也要讲礼貌 🙇♂️
transition 解决的是:组件插入/移除的动效。
也就是:从“有”到“无”,别直接消失得像被外星人抓走👽……
✅ 场景:提示条 Toast/通知横幅“滑入+淡入”“滑出+淡出”
import { Curve } from '@kit.ArkUI';
@Entry
@Component
struct TransitionBannerDemo {
@State show: boolean = false
build() {
Column({ space: 16 }) {
Button(this.show ? '隐藏横幅' : '显示横幅')
.onClick(() => this.show = !this.show)
if (this.show) {
Row() {
Text('📢 更新成功!别忘了点个收藏😄')
}
.padding(12)
.borderRadius(12)
// 👇 过渡:出现/消失
.transition(
TransitionEffect.opacity()
.combine(TransitionEffect.translate({ x: 0, y: -16 }))
)
// 👇 过渡动画节奏
.animation({ duration: 220, curve: Curve.EaseOut })
}
}
.padding(24)
}
}
✅ transition 适合:弹窗、横幅、列表项插入删除、空态切换
⚠️ 注意:transition 的“主角”是组件本身的插入/移除,不是单纯属性变化(别搞混哈😄)
🤌🕺 手势动画:跟手、回弹、阻尼,像“有弹性的世界”🧲
手势动画的关键是两段:
- 跟手:手指动多少,组件就动多少(即时反馈)
- 收尾:松手后用动画回弹/吸附/惯性(手感来源)
✅ 场景:可拖拽的小卡片(松手回弹)
import { Curve } from '@kit.ArkUI';
@Entry
@Component
struct DragCardDemo {
@State offsetY: number = 0
@State dragging: boolean = false
build() {
Column() {
Text('🤌 拖我一下试试(松手会回弹)')
.margin({ bottom: 16 })
.fontSize(18)
Row() {
Text(this.dragging ? '🫣 我被拖着呢...' : '😄 我很乖,快来拖我')
}
.padding(16)
.borderRadius(16)
.translate({ x: 0, y: this.offsetY })
.gesture(
PanGesture()
.onActionStart(() => {
this.dragging = true
})
.onActionUpdate((e) => {
// 跟手:直接赋值,别 animateTo(否则会“滞后感”)
this.offsetY = Math.max(-120, Math.min(200, e.offsetY))
})
.onActionEnd(() => {
this.dragging = false
// 收尾:回弹
animateTo({ duration: 260, curve: Curve.EaseOut }, () => {
this.offsetY = 0
})
})
)
}
.padding(24)
}
}
🧠 手势动画“手感好”的 3 个小秘诀(亲测有效😤)
- ✅ 跟手阶段不要加动画:直接改状态,让它“实时跟着手”
- ✅ 松手阶段再 animateTo:回弹/吸附/惯性都在这里
- ✅ 给移动加边界:没有边界的拖拽会像飘在太空🪐(用户会慌)
🧯🧨 常见翻车现场(来,挨个儿灭火🔥)
😵 1)在列表里对每个 item 开隐式动画:卡到怀疑人生
你想象一下:列表 50 个 item,每个都在 opacity/height 动……
它不是丝滑,它是“群魔乱舞”🕺💥
✅ 建议:
- 只对“关键变化”的 item 动画
- 大批量更新时,减少动画范围,或用更轻的动画(opacity/translate 比 height 更轻)
🫠 2)跟手拖拽还用 animateTo:手指在前,组件在后
这会产生很强的“迟滞感”。
✅ 跟手=直接赋值;松手=animateTo(记住这句就够了😄)
😭 3)transition 没生效:你其实没发生“插入/移除”
如果你只是把 opacity 从 1 改到 0,那是属性动画,不是 transition。
✅ transition 生效的前提:组件真的被 if/ForEach 插入或移除
😤 4)动画太用力:duration 800ms + 大位移 + 弹簧乱跳
用户:你这 App 怎么“演出感”这么强🎭
✅ 建议:常规交互动画 120~280ms 区间最舒服(弹一下可以更短),位移别太夸张。
🎯✅ 总结:做出“系统级手感”的小抄📌✨
你想要动画不土、不卡、不突兀,记住这几条就很稳:
- 🎯 状态驱动 UI:先把“状态变化”理清,再决定怎么动
- ⚡ 显式动画 animateTo 管整体节奏:需要统一过渡就用它
- 🫥 隐式动画别滥用:尤其是列表、复杂布局,省着点用😅
- 🧩 transition 负责出场/退场礼仪:出现/消失要温柔
- 🤌 手势动画分两段:跟手实时、松手回弹
- 🧠 性能优先:能用 opacity/translate/scale,就尽量别狂动 height/复杂布局
🧧福利赠与你🧧
无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学SpringBoot」专栏(全网一个名),bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门SpringBoot,就像滚雪球一样,越滚越大, 无边无际,指数级提升。
最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。
同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板、技术文章Markdown文档等海量资料。
✨️ Who am I?
我是bug菌(全网一个名),CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云多年度十佳博主/价值贡献奖,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;更多精彩福利点击这里;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿。

-End-
- 点赞
- 收藏
- 关注作者
评论(0)