3. TouchGFX基础概念

移动动画,说白了就是让界面元素「动起来」。在嵌入式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 知识体系总览

下面这张图帮你梳理本章的核心知识点:

移动动画知识体系 移动动画 MoveAnimator组件 绝对移动 vs 相对移动 路径动画实现 继承自Image/Box 内置缓动函数 动画完成回调 循环/延迟控制 startMoveAnimation(x, y) startMoveAnimationDelta(dx, dy) 分段移动实现 AnimationManager管理 核心要点 • 绝对移动:目标坐标固定,适合弹窗、按钮等固定位置动画 • 相对移动:基于当前位置偏移,适合滑动、拖拽等连续交互 • 路径动画:分段实现,配合缓动函数和AnimationManager管理复杂路径

3.5 实战技巧与避坑指南

最后分享几个我在项目中积累的经验:

  • 动画时长控制:嵌入式设备的屏幕刷新率一般是60fps,动画时长建议控制在200-500ms之间。太短了用户看不清,太长了显得拖沓。
  • 避免同时移动太多元素:我曾经在一个界面上同时移动10个图标,结果掉帧严重。后来改成分批移动,每次只移动3-4个,流畅度明显提升。
  • 善用缓动函数:不同的缓动函数给人的感觉完全不同。cubicEaseInOut适合大多数场景,bounceEaseOut适合「弹跳」效果,elasticEaseOut适合「弹性」效果。
  • 路径动画的坐标计算:分段路径的坐标最好用相对坐标,这样即使父容器位置变了,动画也不会跑偏。

我曾经踩过的坑:用绝对移动做路径动画时,忘了考虑父容器的偏移。结果动画在模拟器上跑得好好的,烧到板子上就偏了。后来统一改用相对移动,再也没出过这个问题。

嗯,移动动画的内容就这些。你想想看,其实核心就三个东西:MoveAnimator组件、绝对移动与相对移动的区别、路径动画的实现。搞明白了,你的界面就能「动」得自然又流畅。