3、移动动画:MoveAnimator组件、绝对移动与相对移动、路径动画实现
移动动画,说白了就是让界面元素「动起来」。在嵌入式GUI里,这是最基础也最常用的动画类型。我刚开始用TouchGFX时,觉得移动不就是改个坐标嘛,后来才发现里面的门道还真不少。
今天咱们就聊聊MoveAnimator组件、绝对移动与相对移动的区别,以及怎么实现路径动画。嗯,这三个东西搞明白了,你的界面就能「活」起来。
3.1 MoveAnimator组件:移动动画的基石
MoveAnimator是TouchGFX里专门做移动动画的组件。它继承自Image或Box等可视组件,额外增加了动画能力。说白了,就是给普通控件装了个「移动引擎」。
核心特性:
- 支持绝对坐标移动和相对坐标移动
- 内置缓动函数(Easing Equations)
- 可设置动画时长、延迟、循环次数
- 支持动画完成回调
我在项目中遇到过一个问题:用普通Image做移动,每次都得手动计算坐标、刷新屏幕,代码又臭又长。换成MoveAnimator后,三行代码搞定,而且动画流畅度明显提升。
3.1.1 基本用法
先看一个最简单的例子:让一个图标从屏幕左侧滑入。
// 在setupScreen()或某个触发函数中
moveAnimator.startMoveAnimation(50, 100, // 目标X, 目标Y
300, // 动画时长(ms)
EasingEquations::cubicEaseInOut); // 缓动函数
这段代码会让moveAnimator在300毫秒内,从当前位置移动到(50, 100)的位置。缓动函数cubicEaseInOut会让动画开始和结束时速度慢一些,中间快一些,看起来更自然。
我的习惯:缓动函数别用Linear,除非你追求那种「机器人式」的僵硬效果。cubicEaseInOut和quarticEaseInOut是我最常用的,视觉上最舒服。
3.2 绝对移动 vs 相对移动
这两个概念容易混淆。我刚开始也搞错过,结果动画跑到了屏幕外面去。咱们用一张表说清楚:
| 类型 | 方法 | 含义 | 适用场景 |
|---|---|---|---|
| 绝对移动 | startMoveAnimation(x, y, duration, easing) |
移动到屏幕上的绝对坐标(x, y) | 固定位置动画,如弹窗从中心弹出 |
| 相对移动 | startMoveAnimationDelta(deltaX, deltaY, duration, easing) |
从当前位置移动(deltaX, deltaY)的距离 | 滑动、拖拽、连续移动 |
举个例子你就明白了:
// 绝对移动:无论当前在哪,都移动到(100, 200)
moveAnimator.startMoveAnimation(100, 200, 500, EasingEquations::cubicEaseInOut);
// 相对移动:从当前位置向右移动50像素,向下移动30像素
moveAnimator.startMoveAnimationDelta(50, 30, 500, EasingEquations::cubicEaseInOut);
我曾经踩过的坑:用绝对移动做滑动列表时,每次都要计算目标坐标,一旦列表项数量变化,坐标就得重新算。后来改用相对移动,每次只传偏移量,代码简洁多了,也不容易出bug。
3.3 路径动画实现
路径动画,就是让元素沿着一条预设的轨迹移动。比如一个球沿着「S」形曲线滚动,或者一个菜单项沿着弧形展开。
TouchGFX没有内置的路径动画API,但我们可以用「分段移动」来实现。说白了,就是把一条路径拆成多个直线段,依次执行移动动画。
3.3.1 分段路径实现
假设我们要让一个图标沿着「L」形路径移动:先向右走,再向下走。
// 第一步:向右移动100像素
moveAnimator.startMoveAnimationDelta(100, 0, 300, EasingEquations::cubicEaseInOut);
// 第二步:在动画完成回调中,再向下移动80像素
// 需要重写moveAnimator的动画结束回调
moveAnimator.setMoveAnimationEndedCallback([this]() {
moveAnimator.startMoveAnimationDelta(0, 80, 300, EasingEquations::cubicEaseInOut);
});
这样就能实现一个「L」形路径。如果路径更复杂,比如「S」形,可以拆成4-5个分段,每个分段用不同的缓动函数,效果会更自然。
我的经验:分段路径的段数不是越多越好。段数太多,动画会显得「卡顿」,因为每段之间有一个微小的停顿。我一般控制在3-5段,配合合适的缓动函数,效果就很好了。
3.3.2 使用AnimationManager管理复杂路径
如果路径特别复杂,比如需要同时移动多个元素,或者路径有分支,我建议用AnimationManager来统一管理。
// 创建一个AnimationManager
AnimationManager manager;
// 添加多个动画到管理器
manager.add(moveAnimator1);
manager.add(moveAnimator2);
// 启动所有动画
manager.startAllAnimations();
这样做的好处是:可以统一控制动画的启动、暂停、停止,还能监听所有动画的完成状态。
3.4 知识体系总览
下面这张图帮你梳理本章的核心知识点:
3.5 实战技巧与避坑指南
最后分享几个我在项目中积累的经验:
- 动画时长控制:嵌入式设备的屏幕刷新率一般是60fps,动画时长建议控制在200-500ms之间。太短了用户看不清,太长了显得拖沓。
- 避免同时移动太多元素:我曾经在一个界面上同时移动10个图标,结果掉帧严重。后来改成分批移动,每次只移动3-4个,流畅度明显提升。
- 善用缓动函数:不同的缓动函数给人的感觉完全不同。cubicEaseInOut适合大多数场景,bounceEaseOut适合「弹跳」效果,elasticEaseOut适合「弹性」效果。
- 路径动画的坐标计算:分段路径的坐标最好用相对坐标,这样即使父容器位置变了,动画也不会跑偏。
我曾经踩过的坑:用绝对移动做路径动画时,忘了考虑父容器的偏移。结果动画在模拟器上跑得好好的,烧到板子上就偏了。后来统一改用相对移动,再也没出过这个问题。
嗯,移动动画的内容就这些。你想想看,其实核心就三个东西:MoveAnimator组件、绝对移动与相对移动的区别、路径动画的实现。搞明白了,你的界面就能「动」得自然又流畅。