恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
FireMonkey 十种动画插值类型详解与选型指南
首页
资讯中心
/
FireMonkey 十种动画插值类型详解与选型指南
FireMonkey 十种动画插值类型详解与选型指南
发布时间:2026/10/1 17:08:44
前阵子我把一个页面切换动画从 Linear 改成 Quad用户第一时间反馈说“速度快了不少”。实际上动画时长一秒没改我只是换了TFloatAnimation的Interpolation属性。FireMonkey 动画的观感差异很大程度由插值类型决定这也是最容易被人忽略的一项设置。这篇文章就把十种插值类型逐个拆开配上行之有效的代码和实操截图级别的说明希望能帮你在做界面动效时不再“只调时长、不会调手感”。1. 动画插值到底是什么1.1 从“开始值到结束值”到“中间过程”FireMonkey 里做动画最常见的套路是给控件的某个属性指定起始值和终止值然后让引擎自动完成中间的过渡。比如把一张矩形的位置从屏幕左侧移到右侧代码里只需要写StartValue和StopValue剩下的时间轴推进都由 FMX 负责。问题就在这里从开始到结束的中间过程存在无数种走法可以是匀速直线到达也可以开头慢、结尾快还可以先冲过头再拉回来。插值类型Interpolation Type决定的就是这条中间路径的形状。FMX 引擎会把动画时间归一化成 0 到 1 之间的数值0 代表动画刚开始1 代表动画结束插值函数则把这个时间比例映射成进度比例。如果映射关系是直线就是线性插值如果映射关系是曲线那就会出现加速、减速、反弹、震荡等不同视觉结果。用生活化的例子理解你从一个路口走到另一个路口匀速走是一回事先慢悠悠起步、后半程狂奔是另一回事先往后退两步再冲刺又是完全不同的感觉。三者最终都到达同一个路口但路上的节奏完全不一样。FireMonkey 里的TInterpolationType就是专门用来控制这个“走路节奏”的东西。1.2 FMX 的动画模型与归一化时间在 FireMonkey 中所有动画都继承自TAnimation比如我们最常用的TFloatAnimation、TColorAnimation、TBitmapAnimation。它们内部计算时会把动画时长Duration切成一段一段并在每一帧根据当前时间计算出NormalizedTime。NormalizedTime是一个 0 到 1 之间的值0 表示起点1 表示终点。然后 FMX 会把NormalizedTime交给当前生效的插值器。默认的插值器是TLinearInterpolator它会原封不动地返回这个时间比例所以进度和时间严格同步。而其他插值器会返回一个不同于输入时间的值有的返回比输入时间更小的数导致动画前期推进缓慢有的返回比输入时间更大的数导致动画前期就冲出去一截。理解了这一层再去看每种插值类型的曲线图时你就能知道曲线越高代表同一时间点上推进得越多。我刚开始学 FireMonkey 时总把“动画时长”等同于“动画快慢”。后来才发现时长决定的是总跨度插值类型决定的是跨度的分布。同样的两秒动画用 Exponential 和用 Linear 的肉眼观感几乎像是两个不同时长的动画这就是插值类型的魅力所在。2. FireMonkey 动画的最小实现框架2.1 一个可以直接跑的 FloatAnimation 示例学习插值类型之前先搭一个最简动画架子。下面这段代码让一个名为Rectangle1的矩形在水平方向移动这是我在测试插值时最常用的最小示例procedure TForm1.DoMoveRectangle; var Anim: TFloatAnimation; begin Anim : TFloatAnimation.Create(Rectangle1); Anim.Parent : Rectangle1; // 动画对象挂到控件上 Anim.PropertyName : Position.X; // 要属性动画的字段 Anim.StartValue : Rectangle1.Position.X; Anim.StopValue : ClientWidth - Rectangle1.Width - 20; Anim.Duration : 1.0; // 单位秒 Anim.Interpolation : TInterpolationType.Linear; Anim.Start; end;这是一个很标准的TFloatAnimation用法。注意Parent和PropertyName是 FireMonkey 动画的两个关键设置Parent决定动画接收者是谁PropertyName决定要改变哪个属性。这里改变的是Position.X所以StartValue和StopValue都是数值型。如果换成RotationAngle那动画就会变成旋转。很多新手会忘记设置Parent结果动画对象虽然创建了却不会驱动任何控件界面一片寂静。另外动画默认不会在结束后自动释放你可以在OnFinish事件里调用Free或者像我这样在页面关闭时统一释放。2.2 把插值类型接到动画上把上面代码里的Interpolation从Linear改成其他枚举值比如Quad、Cubic、Bounce就能直观感受到动画节奏的变化。如果你想在运行时切换插值类型最简单的方式是配合下拉框procedure TForm1.ComboBox1Change(Sender: TObject); begin if Assigned(CurrentAnim) then CurrentAnim.Interpolation : TInterpolationType(ComboBox1.ItemIndex); end;下拉框的 ItemIndex 和枚举值对应关系需要注意。如果不想依赖枚举顺序可以在初始化时按枚举顺序填列表for var LType : Low(TInterpolationType) to High(TInterpolationType) do ComboBox1.Items.Add(GetEnumName(TypeInfo(TInterpolationType), Ord(LType)));这里用到了System.TypInfo的GetEnumName它可以保证下拉项和枚举值严格一一对应省去手工维护列表的麻烦。运行时改插值的好处是你能在同一个界面上反复对比十种类型不需要每次都重新编译这也是后面做交互调试台的基础。3. 十种动画插值类型图解与实测3.1 Linear 和 Quad从匀速到匀加速我先从最常用的两类说起。Linear 是最直观的插值时间走到 50%进度也刚好是 50%。它没有任何加速或减速适合进度条、滚动提示这类需要稳定节奏的场景。缺点也很明显如果用在弹窗动画上开头的移动会比较机械缺少柔和感。Quad 则是一条二次曲线同一时间点上前期推进量小于时间比例后期推进量大于时间比例。举个例子动画进行到 50% 时Linear 已经走了一半路Quad 可能只走了四分之一左右所以你会觉得它“起步慢、后段猛”。这种节奏非常适合卡片入场、按钮位移等需要一点“冲劲”的场景。如果目标是从底部滑出的面板用 Quad 会比 Linear 舒服得多。我在实际项目中移动端列表项入场动画常选 Quad。原因很简单列表项大多从屏幕边缘滑入用户注意力在开头速度慢一点能让人看清内容等后半段速度上来又能营造流畅的视觉感受。这个“先慢后快”的节奏特别适合营造“被推入画面”的感觉。3.2 Cubic、Quart、Quint加速感一级比一级强Cubic 是三次曲线加速感比 Quad 更强烈。在移动端转场动画里Cubic 是很多设计师喜欢的效果开始几乎不动中间开始明显提速结尾干脆利落。用文字曲线示意大概长这样Linear : ▁▂▃▄▅▆▇█ Quad : ▁▂▃▄▅▆▇█ Cubic : ▁▂▃▄▅▆▇█ Quart : ▁▂▃▄▅▆▇█ Quint : ▁▂▃▄▅▆▇█严格来说上面图示只是示意方向实际曲线的弯曲程度是递增的。Quart 是四次曲线Quint 是五次曲线后者在动画前半段几乎处于“原地踏步”状态然后在最后三分之一时间里快速冲到终点。这种极端加速感不常用但用在数字跳动、倒计时结束、成就解锁这类强调“最后一刻爆发”的场景时效果非常惊艳。需要提醒的是Quart 和 Quint 千万别和长时长的动画混用。如果动画超过三秒前几秒几乎看不到移动用户可能会以为程序卡死了。我踩过这个坑曾经把一个成就动画配成 Quint前两秒完全没有反馈结果用户反复点按钮最后来了个瞬移感极强的加速体验很糟糕。后来我把它和时间控制在 800 毫秒以内才找到合适的节奏。3.3 Sine 和 Exponential丝滑过渡与爆发冲刺Sine 的类型会先加速后减速像一条 S 形曲线。它的特点是开头不会太慢结尾也不会突然刹死过渡非常平滑。这种插值在页面淡入淡出、图标平滑移动、进度圆环等场景中非常通用可以说是“万金油”。很多 UI 框架里的 ease-in-out 设计本质就是接近 Sine 的效果。如果你不知道选什么插值类型选 Sine 通常不会出错。Exponential 则是指数曲线它的视觉特征是前期几乎停滞、中期突然加速、后期极快收尾。这个性质让它很适合做“从 0 开始冲刺到满值”的进度效果比如下载完成、加载完成、技能释放。用数据举例动画进行到 50% 时Linear 推进了 50%Exponential 可能才推进不到 10%但到 70% 时间点时可能已经推进到 60%最后 10% 时间则完成剩余冲刺。使用 Exponential 要特别留意终点附近的边界感。因为它结尾极快如果用户需要看清目标位置的落点很容易错过。我在做一个充值成功动画时数字翻转用了 Exponential金币图标飞向余额区域时用了它但最后落点的“弹跳反馈”是用另一个 Bounce 动画补上的不然只看指数冲刺会觉得太突然。3.4 Back、Bounce、Elastic回拉、反弹与弹性震荡这三类都有明显的“超出目标”行为适合做有个性的提示动画。Back 的视觉效果是“先反向运动一段距离再朝目标方向前进”。想象拉弓射箭箭会先往后拉一点再飞速射出去。用在卡片弹出上Back 会让卡片先从中心稍微缩小或从原始位置稍微移动再弹到最终位置整个过程有一种“蓄力感”。实际使用时注意反向位移不能太大否则用户会产生“点错了”的错觉。Bounce 模拟的是物体落地后的反弹。动画进行到接近终点时进度会先在终点附近上下波动再稳定下来。这种插值做“重量感”特别好比如按钮掉落到面板上、消息气泡落地、角色跳跃落地后的回弹。Bounce 的反弹次数和幅度是内置设计好的通常第一下反弹最明显后续越来越小。Elastic 更像弹簧或琴弦震荡它会围绕目标值来回振荡很多次直到逐渐停下来。这个效果视觉冲击力最强适合红包弹出、勋章解锁、节日撒花之类的场景。但也是最容易做过头的一个振荡次数多、振幅大时页面会显得非常杂乱甚至让用户觉得头晕。我在项目里只用它做一次性提醒动画并且严格控制振幅不超过目标值的 8%。这三者还有一个共同坑点它们都会有“短暂越界”的过程。对位置类动画来说越界会让控件跑到目标位置之外如果父容器有裁剪ClipChildren动画边缘会被切断对透明度动画来说越界会溢出到大于 1 或小于 0 的数值FMX 虽然会做钳制但你该知道这是正常现象不是控件坏了。3.5 一张表总结十种类型的选型参考插值类型视觉感受适合场景不宜场景Linear匀速、机械进度条、滚动指示弹窗、强调动画Quad先慢后快卡片入场、面板滑入长时间等待动画Cubic加速感明显页面转场、强调位移实时反馈型按钮Quart强烈加速数字冲刺、成就解锁超过 3 秒的长动画Quint极端加速宣传片级别的终点击打日常 UI 动效Sine平滑加速再减速淡入淡出、常规位移需要突然爆发的感觉Exponential前段极慢后段爆发加载完成、技能释放需要持续反馈的场景Back先反向再正向卡片弹入、图标激活频繁切换的页面Bounce落地反弹重量提示、消息掉落精密数据界面Elastic弹性震荡红包弹窗、节日特效高频滚动列表这张表不是标准答案但可以帮你少走弯路。实际项目里我经常拿 Sine 做默认用 Quad 做入场用 Bounce 做落地反馈用 Elastic 做一次性高亮提示。这个组合在大多数移动端和桌面端界面里都足够体面。4. 实操做一个运行时插值试玩台4.1 界面布局思路纸上谈兵没意思最好的学习方式是在一个界面里同时跑十种插值。我建议你新建一个 FMX 项目在窗口上放一块TRectangle作为移动目标再放一个TComboBox用于选择插值类型一个TButton负责触发动画再加一条TLabel显示当前动画进度。布局并不复杂关键是三个控件之间的数据流要打通。下拉框初始化可以写成这样procedure TForm1.FormCreate(Sender: TObject); var LType: TInterpolationType; begin for LType : Low(TInterpolationType) to High(TInterpolationType) do ComboBox1.Items.Add(GetEnumName(TypeInfo(TInterpolationType), Ord(LType))); ComboBox1.ItemIndex : 0; end;这样列表框会列出十种插值类型并且顺序和枚举值严格对应。运行时选到哪一项动画就使用哪一种插值。4.2 核心切换代码与事件绑定触发动画的按钮里我每次会先清掉旧的动画避免上一次动画没结束后又叠加一个新动画导致控件位置错乱。然后创建新的TFloatAnimation把插值类型直接赋给动画对象procedure TForm1.btnRunClick(Sender: TObject); var i: Integer; Anim: TFloatAnimation; begin for i : Rectangle1.Animations.Count - 1 downto 0 do Rectangle1.Animations[i].Stop; Anim : TFloatAnimation.Create(Rectangle1); Anim.Parent : Rectangle1; Anim.PropertyName : Position.X; Anim.StartValue : 20; Anim.StopValue : ClientWidth - Rectangle1.Width - 20; Anim.Duration : 2.0; Anim.Interpolation : TInterpolationType(ComboBox1.ItemIndex); Anim.OnProcess : TrackProgress; Anim.AutoReverse : False; Anim.Start; end;OnProcess事件会在动画执行过程中持续触发我们可以用它实时更新屏幕上的进度文字这样就能清楚看到每种插值在不同时间点的推进情况procedure TForm1.TrackProgress(const Sender: TObject); var LAnim: TFloatAnimation; begin LAnim : Sender as TFloatAnimation; Label1.Text : Format(插值类型: %s 时间: %.2f 进度: %.0f%%, [ GetEnumName(TypeInfo(TInterpolationType), Ord(LAnim.Interpolation)), LAnim.NormalizedTime, LAnim.NormalizedTime * 100 ]); end;这个“试玩台”跑起来后你可以挨个切换下拉项观察矩形在屏幕上从左向右的运动轨迹。你会明显看到 Linear 是匀速的Quad 前期基本不动Bounce 在终点附近反复弹跳Elastic 像一条松开的橡皮筋一样晃来晃去。这种归纳式对比比查阅任何文档都印象深刻。4.3 如何进一步观察曲线细节如果你想把曲线看得更直观可以把矩形换成一个TPath或TLine并在OnProcess中把NormalizedTime和当前进度投射到二维坐标上逐帧画点形成一条实时曲线。更简单的办法是让矩形做纵向移动然后再横向移动两轴分别接入不同插值组合起来你就能看到弧线路径。做这种调试台的时候我强烈建议把Duration故意设长一点比如 2 到 3 秒。因为插值类型之间的差异在低速下才容易看清设置太短会眨眨眼就结束了根本分不出 Exponential 和 Quint 的区别。还有一个实用技巧把AutoReverse打开你可以看到动画从起点到终点再到起点的完整往返过程。部分插值类型比如 Bounce、Elastic在反向过程中会和正向过程完全不一样这也会提醒你入场动画和退场动画不应该直接暴力反转。有些效果只适合“进出”其中一个方向。5. 常见问题与排查技巧5.1 几个典型坑和处理方法问题原因解决方式动画不生效没有设置 Parent将动画的 Parent 设为要驱动的控件属性值一直跳动动画对象叠加创建新动画前停止旧的动画Quad/Bounce 在布局中被裁剪父容器裁剪了越界内容调整父容器尺寸或关闭 ClipChildrenElastic 看起来乱掉振幅过大或时长过长降低动画幅度、缩短到 1 秒内切换插值类型后没变化动画已经运行中停止并重新创建动画再设置色值动画出现奇怪颜色使用 ColorAnimation 时插值不当优先用 Linear 或 Sine最容易被忽视的是第一个Parent。FireMonkey 的动画不像传统 Timer 回调那样直接操作控件而是寄宿在控件树里每帧由全局动画管理器统一驱动。如果你没设置 Parent动画对象就是游离的什么都不会发生。创建动画时最好传入控件的实例作为 Owner并且把 Parent 赋给控件这是双保险。叠加问题也经常遇到。当一个动画还在运行用户再次点击按钮触发新动画时两个动画同时改变同一个属性属性值会被后来居上的动画覆盖。表现就是控件卡在奇怪位置甚至来回抖动。解决办法是每次创建前先遍历Rectangle1.Animations把已经存在的动画Stop掉。这个习惯我在所有 FMX 动效项目里都保持。5.2 与时长、延迟和重复的组合经验插值类型不是孤立工作的它经常和Duration、Delay、AutoReverse、RepeatCount一起配合。我总结出的经验是插值曲线越“激烈”动画时长越要短。Elastic 通常控制在 600 到 1000 毫秒Bounce 控制在 800 到 1400 毫秒Back 控制在 300 到 600 毫秒。如果时长超过两秒再美的曲线也会显得拖沓尤其是带越界效果的插值会让用户觉得系统不稳定。延迟启动也很关键。页面初始化时多个元素要依次入场我常给第二个、第三个元素设置Delay : 0.1 * Index让入场动画像波浪一样逐层出现。插值类型选择 Sine 或 Quad 会比较合适因为延迟序列本身已经营造了节奏感再用强烈的 Elastic 反而会互相干扰。重复播放时要注意RepeatCount配合 Bounce 或 Elastic 会产生连续弹跳效果很容易让界面显得焦虑。我只建议在“等待连接”“加载中”这类持续反馈场景使用 Linear 或 Sine 的循环动画并且重复次数不要无限通常 3 到 5 次再停止比较自然。5.3 给初次接触插值的开发者的上手指引如果你是第一次接触 FireMonkey 动画插值不建议一口气把十种类型全部背下来。先掌握三个就足够覆盖日常 80% 的需求Linear、Quad、Sine。Linear 让你理解动画驱动的基本逻辑Quad 帮你体会“加速感”Sine 让你知道“平滑过渡”是什么感觉。剩下七个里你只需要在做个性和强调效果的场景中再回头查。上手路径可以这样安排先跑通最小示例把矩形的位置动画跑起来再换成 Quad观察前后段速度差然后把Duration从 2 秒改成 0.5 秒感受加速度被压缩后的变化最后加入 Bounce 和 Elastic用“过头再回来”的效果强化印象。整个流程用一个下午就能完成投入产出比非常高。在真实业务中动画插值不是越快越好也不是越花哨越好。它更像食材里的调味品合适最重要。界面动效的目标是让用户感到自然、清晰、顺畅而不是用花哨的弹跳秀技术。我在实际项目里的习惯是把一张插值速查表放在团队共用文档里每个动画都要写明“为什么选这个插值类型”。这样做了一年多之后团队成员写动效的效率明显提升因为大家在选型时不再凭感觉而是先问自己想表达什么情绪是想让元素“自己滑出来”还是“被推出来”还是“从天上掉下来”下午调试那个试玩台时我发现同一段 500 毫秒的动画用 Quint 时矩形前半程几乎纹丝不动正当我怀疑代码写错它在最后 150 毫秒里突然冲出去那一瞬间把旁边同事都吓了一跳。那次之后我彻底明白文档里写“插值类型决定动画节奏”这句话实在太轻描淡写了它决定的是用户对界面性格的感知。