ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FireMonkey动画实战:用动画组件为Delphi窗体添加动态效果

FireMonkey动画实战:用动画组件为Delphi窗体添加动态效果 做Delphi开发这么多年如果要让我挑一个最能提升界面质感、投入产出比又极高的功能FireMonkey的动画系统绝对排在前三。几乎每个第一次接触FMX窗体的人都会被那种丝滑的过渡效果吸引窗体淡入淡出、面板从侧边滑入、弹窗带一点柔和的缩放……而这些效果并不需要引入第三方控件也不用自己去写复杂的定时器或插值算法FireMonkey内置的动画组件就能直接搞定。尤其是配合Delphi 12里更完善的源码和跨平台能力用上动画组件之后窗体的动态效果能很自然地融入业务场景比如登录窗淡入、设置面板滑出、消息提示缩放弹出。这篇文章就是基于“一步一步学习使用FireMonkey动画使用动画组件为窗体添加动态效果”这个核心需求来写的。适合谁看刚接触FMX的Delphi开发者或者已经在用VCL、想给窗体增加现代感动态效果的同行。我用实际做过的几个窗体效果作为例子从原理到参数再到完整操作步骤一起拆开讲保证看完就能在自己项目里落地。先说一个我自己常有的感受很多人以为动画是“锦上添花”的装饰但真正用好了它其实是界面交互的一部分能告诉用户“发生了什么变化”。比如页面切换时新内容滑入的方向能暗示层级关系弹窗淡入淡出能区分模态和非模态操作。FMX的动画组件最大的好处是“属性驱动”——不需要手动管理帧循环只要指定起始值、结束值和时间框架自动帮你做中间插值。这种思路非常直观也容易出效果。接下来我从原理、参数、实操到排查把整个FMX动画组件的使用路径完整过一遍。1. 先搞清楚FireMonkey动画的底层逻辑再动手1.1 属性动画的本质它驱动的是任意对象的任意属性FireMonkey的动画系统和我最早接触的VCL完全不同。VCL时代想做动画基本是Timer 手动改属性每触发一次Tick改一点非常繁琐而且容易闪烁。FMX的做法是属性动画Property Animation你告诉它“从A变到B时间多久”系统自动计算中间帧并刷新界面。这意味着什么呢在FMX里几乎可见控件都继承自TFmxObject家族而这些控件暴露出的数值型属性Opacity、Position.X、Scale.X、RotationAngle、Margins.Top等等都可以作为动画驱动目标。换句话说只要是控件上能找到的数值属性就可以做动画。这正是“为窗体添加动态效果”最核心的底层支持窗体本身也是一个控件所以窗体的透明度、位置、大小、缩放都能被动画组件接管。实际还有更灵活的用法动画组件不只是绑定控件也可以绑定一些非可见对象的属性。比如绑定一个TGradient的Color属性就能让背景颜色平滑过渡。TFloatAnimation支持几乎所有Single类型的属性而TColorAnimation专门处理TAlphaColor类型的颜色值TAnimator类还提供了一组静态方法不需要在设计器里拖组件就能直接用代码控制动画。1.2 四大内置动画组件怎么分工FMX内置了几种动画组件常用的就四个把它们的分工理清楚选型就不会错。TFloatAnimation最常用负责所有浮点数值属性的动画包括Opacity透明度、Position.X/Y、Scale.X/Y、RotationAngle、宽度高度等。这个组件是绝大多数窗体动态效果的主力。TColorAnimation负责颜色过渡例如窗体背景色从浅蓝渐变到白色、跑马灯式的边框变色、提示信息的背景闪烁等都靠它。注意它针对TAlphaColor类型不是TColor。TGradientAnimation专门处理渐变对象适合做背景渐变的动态切换比如登录页背景从深色渐变为浅色。TPathAnimation可以让控件沿着一条路径移动适合更复杂的轨迹动画比如弹窗从屏幕角落飞入。除了这几个组件还有一个经常被忽略的TAnimator类。它不是组件是一个静态工具类提供AnimateFloat、AnimateColor等静态方法。用代码临时做一个动画时非常方便不需要在设计器里放组件。后面我会在实操环节里给出它的代码示例。1.3 动画组件的触发方式图省事还是图可控FMX动画组件的运行机制很灵活主要由Trigger、TriggerTarget、Enabled和事件来协调。这里有一个常见的误区新手往往以为把动画组件放进窗体它就会自动播放结果发现运行时毫无反应。实际上动画组件分为“手动触发”和“自动触发”两类工作方式。自动触发靠Trigger属性取值是字符串例如IsVisible、IsFocused、IsMouseOver等。系统会在TriggerTarget指定的对象上检测这些状态变化一旦满足条件就自动Start动画。例如你想让Panel在鼠标悬停时放大可以在动画组件上设置Trigger IsMouseOverTriggerTarget设为该Panel这会省去写事件处理。但这种方式的缺点是你不一定能精准控制动画的播放方向和时机。手动触发则更可控通常使用Start、Stop、StartValue等运行时方法。比如点击按钮后让子窗体滑入或淡入这时可以完全控制开始时间和结束值。我的建议是登录窗这类固定流程可以用Trigger自动播放业务里需要与用户操作紧密配合的动画一律手动触发。因为自动触发经常会在你不想要的时候跳舞比如窗体启动时多个Trigger同时命中动画互相打架。2. 核心参数逐个拆解把动画调到“刚刚好”2.1 Duration、StartValue和StopValue先算再填这三个参数是每个动画组件都有的。Duration单位是秒决定整个动画持续多长时间。StartValue是动画开始时的属性值StopValue是结束时的值。它们看似简单但经验往往体现在数值怎么定。透明度动画窗体从完全透明到不透明StartValue 0StopValue 1Duration 0.4。这组值在我做过的项目里几乎通用0.4秒既不会觉得拖沓又足够柔和。如果想做出“呼吸感”把Duration放宽到1.5左右配合AutoReverse就有循环效果。位置动画Panel从屏幕右侧滑入StartValue为Panel的X坐标初始位置StopValue为最终位置。这里有一个容易被忽略的细节如果你在设计器里放置的是非Layout对齐的控件动画组件内部是直接修改Position。但如果控件的Align属性是Right或Left直接改Position往往无效因为对齐布局系统会强制覆盖坐标。后面排查章节我会再详细展开这个问题。缩放动画以窗体的Scale.X/Y为属性从0.8到1.0。注意FMX的Scale变换默认围绕控件的左上角而不是中心点。想让弹窗从中心缩放弹出必须先把RotationCenter或ScaleCenter设为0.5, 0.5。这一点初学者几乎必踩后面我给出具体操作。时间也值得琢磨场景差异。窗体淡入0.3秒合适通知气泡类可以从0.15秒起而抽屉式页面滑入用0.5秒会有高级感。最快的方式是先按经验值跑一遍再微调Duration。2.2 Interpolation插值模式决定动画手感的关键同一组StartValue和StopValue配上不同的插值模式最终动画效果可能天差地别。FireMonkey的Interpolation属性提供了Linear、Sinusoidal、Quadratic、Cubic、Exponential、Elastic等模式。Linear是线性插值速度恒定适合颜色变化或进度条这类需要均匀变化的场景。但我个人很少用它做位移和缩放因为机械感太强——真实世界里的运动都是有加速和减速的。Sinusoidal是正弦插值速度是先快后慢近似一个“由动到静”的缓出效果。这是我最常用的模式适合窗体淡入、弹窗缩放给人一种自然阻尼感。Quadratic和Cubic分别是一次和三次多项式插值加速或减速效果更明显。如果想让Panel滑入时有明显的加速冲击感选Quadratic想让对象快速“甩”到终点并戛然而止选Cubic。Exponential是指数插值起始非常慢然后越来越快适合强调“离场”的动画例如窗体退出时的快速缩小消失。Elastic是弹性效果会有回弹像一个球落地后弹几下。适合趣味性场景比如消息气泡弹出时的轻微抖动但业务系统里尽量慎用它会干扰用户对信息稳定性的感知。我在做实际项目时有个习惯给团队定了默认值——Opacity、Color类的动画用SinusoidalPosition、Scale类的动画也先用Sinusoidal再根据观感在Quadratic和Cubic之间切换。Linear就留给颜色相关效果。2.3 AutoReverse、Loop与事件回调的配合动画组件还有两个高频属性AutoReverse和Loop。AutoReverse控制动画到达StopValue后是否自动反向播放回StartValueLoop控制是否无限循环。它们非常适合做持续性的提示效果比如“加载中的呼吸灯”“待命状态的脉冲环”。但自动反向有个坑动画结束时属性停留在StartValue还是StopValue取决于播放轮次。AutoReverse播放一轮后属性回到起始值如果你在结束事件里读取当前属性值来判断状态需要小心。为了避免状态混乱我建议Loop和AutoReverse只用于纯装饰性效果业务状态切换还是用手动触发自己控制Start和Stop。事件方面动画组件提供了ProcessAnimation事件和OnFinish事件。OnFinish在动画播放完一轮后触发经常用在“动画结束后再做下一个动作”的场景例如淡入完成后让输入框获取焦点。如果动画是Loop的OnFinish不会触发这时需要监听ProcessAnimation或者用计时器检查状态。关于代码里动态创建动画组件这里给一个实用的方式uses System.SysUtils, System.Types, FMX.Types, FMX.Ani; procedure TForm1.AnimateOpacity(const AControl: TFmxObject; const AOpacity: Single); var Anim: TFloatAnimation; begin Anim : TFloatAnimation.Create(AControl); Anim.Parent : AControl; Anim.PropertyName : Opacity; Anim.StartValue : AControl.GetObject.Property(Opacity).GetObject.AsTypeSingle; Anim.StopValue : AOpacity; Anim.Duration : 0.4; Anim.Interpolation : TInterpolationType.Sinusoidal; Anim.Start; end;这个方法的好处是不动设计器可以动态为任意控件生成动画组件。注意Create的Owner参数填的是AControl本身这样当控件销毁时动画组件会自动释放不会泄漏。很多初学者会直接用Self作为Owner结果控件释放了动画组件还活着引发空引用。2.4 透明窗体与Panel TopLevel子窗体的特殊场景最近在Delphi社区里透明窗体、Panel做子窗体的讨论很多。这两个场景特别适合结合动画也特别需要注意额外参数。透明窗体通常需要设置窗体StyleBook为空、TransparencyTrue、Fill.Kind None并且去掉边框。在透明窗体上做动画Opacity效果会作用于整个窗体但要注意如果窗体内部有Panel作为内容容器你希望淡入的是窗体还是Panel从性能角度我建议把透明度动画作用在Panel上而不是窗体本身。因为窗体级别的Opacity会触发整个窗体重绘而Panel级别的动画只需要重绘局部区域。用Panel模拟子窗体切换时动画目标通常是Panel的Position.X或Margins.Left。FMX里TForm有TopLevelControls机制但直接把多个Panel放到同一个窗体上用动画切换它们的Offset是更高效的做法。做法是把多个Panel叠放让非活跃Panel的Opacity设为0动画时把Opacity拉回1并改变位置。这样不必创建真正的子窗体也能达到类似页面切换的效果。这里有个关键点如果Panel使用了AlignClient直接动画Position是不行的因为布局系统会覆盖坐标。解决方法是嵌套一个容器外层Panel负责布局AlignClient内层Panel负责动画Position和Opacity都由动画控制。这个嵌套思想在FMX里非常重要几乎所有复杂动画布局都要靠它。3. 一步一步给窗体添加动态效果四个可直接抄作业的实例3.1 效果一窗体淡入淡出这个效果最基础几乎所有工具类软件都会用到。我以主窗体Form1为例目标是让它启动时淡入关闭时淡出。首先在设计器里给Form1添加一个TFloatAnimation我习惯命名为FloatAnimFormOpacity。PropertyName设为OpacityStartValue设为0StopValue设为1Duration设为0.4Interpolation选Sinusoidal。注意一点窗体默认Opacity是1如果想让启动时淡入还需要在窗体的OnCreate或FormShow事件里先把Opacity设为0。这里有一个容易出错的地方窗体的OnShow事件每次切换窗口都会触发如果你的设置面板在窗体显示时把Opacity设为0再配合动画播放可能会导致每次激活窗体都闪烁。更稳妥的方式是在OnCreate里或者在第一次显示之前用PostMessage机制延迟处理确保动画组件在窗体句柄创建完成后才启动。关闭时淡出更讲究。如果在FormClose里做淡出动画可能因为窗体销毁太快动画只播了一半。我的做法是拦截WM_CLOSE或者重写Close行为先用动画把Opacity降到0然后在OnFinish事件里调用真正的Close。也就是“手动不给窗体立即关闭先播完动画”。procedure TForm1.CloseAnimated; begin FloatAnimFormOpacity.StartValue : Self.Opacity; FloatAnimFormOpacity.StopValue : 0; FloatAnimFormOpacity.OnFinish : DoRealClose; FloatAnimFormOpacity.Start; end; procedure TForm1.DoRealClose(Sender: TObject); begin Self.Close; end;这段代码需要说明的是StartValue不是写死1而是读取当前Opacity。因为用户可能在界面停留很久中途可能通过代码改过Opacity或者弹窗未完全显示时就点了关闭这时如果从1开始播放画面会突然闪亮一下。3.2 效果二缩放弹出Modal风格的友好感第二个常用效果是缩放弹出通常用于自定义消息框、确认框、设置面板。目标是让一个Panel从0.85倍大小柔和的放大到1倍同时透明度从0变为1整个弹出过程大约0.3秒。这里就要用到Scale动画。FMX控件的Scale属性是TScale类型包含X和Y两个分量。动画组件无法直接驱动TScale类型所以要分别建两个TFloatAnimation一个PropertyName设为Scale.X另一个设为Scale.Y。缩放中心也需要单独设置否则会从左上角缩放看起来像是窗角抽搐。正确的做法是先把Panel的ScaleCenter.X和ScaleCenter.Y都设为0.5这代表缩放的中心点位于Panel正中央。实际操作中我还会给这个效果加一个ColorAnimation做背景遮罩的淡入。在Panel下面放一个矩形Rectangle设置圆角Fill颜色为白色。ColorAnimation将矩形的透明度从0动画到0.8形成遮罩效果。这样看起来就像原生模态弹窗一样背景变暗、内容从中心放大用户注意力自然聚焦到弹窗上。这个效果的Trigger设置建议完全用手动。在打开弹窗按钮的OnClick里先设置Panel.Visible : TrueOpacity : 0Scale.X : 0.85Scale.Y : 0.85然后依次启动ColorAnimation遮罩淡入、FloatAnimation淡入、Scale动画。关闭时反向播放。动画之间“串联”的顺序也有讲究。我习惯让遮罩先淡入大约0.15秒随后内容Panel开始放大0.2秒形成“背景先沉、内容后起”的层次感。如果同时播放两个动画的起始节奏会互相冲突视觉上显得发飘。3.3 效果三滑入滑出配合Panel做子窗体切换再进阶一点用动画改造表单页面切换。很多人问过“Panel TopLevel子窗体”怎么用其实FMX里做类似WinForm TabControl的页面切换最顺手的方案就是叠放Panel加位置动画。我这里说的结构是窗体上一个根容器里面放两个Panel分别是PanelLogin和PanelMain。初始PanelLogin可见PanelMain不可见。切换时PanelMain先设置Left位置等于窗体的宽度也就是SlideInStart再设置Visible : TrueOpacity从0变到1Position.X从窗体宽度动画到最终位置0形成从右边滑入的效果。原来的PanelLogin向左滑出或直接淡出。关键点在Align处理。我的经验是PanelMain不要直接设置AlignClient否则位置动画无效。给PanelMain外面再套一层容器比如一个Rectangle这个Rectangle保持AlignClient位置不动真正执行动画的是PanelMain本身。举一个具体的参数组合。窗体宽度假设800PanelMain.Width也是800切换开始时PanelMain.Visible : True; PanelMain.Opacity : 0; PanelMain.Position.X : 800; // 从右侧屏幕外开始然后动画StopValue设为0Scale.X和Scale.Y同时从0.98放大到1形成“滑入轻微放大”的双重效果。整个动画Duration设0.5秒Interpolation设Cubic会有明显的“先加速后减速”的拖拽质感。这种组合视觉效果非常接近移动端页面滑动切换。如果你的需求是侧边抽屉原理一样只是把方向从左右改成上下、从改Position.X改成Position.Y。3.4 效果四背景色渐变的透明窗体这个效果结合了最近热词里的透明窗体和背景图需求。需求背景通常是窗体无边框、Transparency设为True、背景色从一种颜色平滑过渡到另一种颜色中间还能显示半透明背景图。透明窗体的核心设置先说清楚Form1.Transparency : TrueForm1.Fill.Kind : TFillKind.NoneForm1.BorderStyle : TFormBorderStyle.None。如果还用StyleBook透明窗体效果会被StyleBook覆盖所以通常要确保StyleBook是空的或者不设置。背景色渐变用TColorAnimationPropertyName设为Fill.Color。注意你是给窗体还是给窗体内部的Rectangle做动画如果目标是窗体本身的Fill那么PropertyName是Fill.Color。如果窗体内部有一个Rectangle用来盖住整个客户区则属性名是Fill.Color动画组件Parent放到Rectangle上。这里有一个透明窗体特有的坑大多数情况下Windows窗口系统原生不支持窗口内某个区域的透明度渐变FMX是通过分层窗口和重绘模拟透明度。当你把窗体Transparency设为True后界面上的所有文字和控件都会被当作一张位图的一部分参与混合。如果里面嵌入了系统原生控件如TEdit的WinOS版本可能不支持透明混合。FMX中在透明窗体上优先用FMX原生控件避免使用Windows原生TEdit、TComboBox在Transparency窗口上出现黑边。我做播放器控件面板的时候就遇到这个情况底部操作栏的背景色要随播放状态变化播放中偏红暂停偏蓝。用TColorAnimation加上Loop和AutoReverse从0.8秒到0.8秒来回渐变整体效果非常自然用户感知不到颜色跳变。3.5 用代码批量创建动画摆脱设计器拖控件有些场景从后台返回的状态需要动态决定动画的目标值比如不同用户权限展示不同的引导动画或者列表项多到不可能在设计器里逐项设置。这时推荐用TAnimator的静态方法它用一个方法调用就能完成一次动画不需要在窗体上增加额外组件。TAnimator类位于FMX.Ani单元最常用的是AnimateFloatuses FMX.Ani, FMX.Objects; procedure TForm1.AnimatePanel(APanel: TPanel); begin TAnimator.AnimateFloat(APanel, Opacity, 1.0, 0.5, TAnimationType.Out, TInterpolationType.Sinusoidal); TAnimator.AnimateFloat(APanel, Scale.X, 1.0, 0.4, TAnimationType.Out, TInterpolationType.Back); end;这些静态方法会自动创建并释放临时动画组件省去手工管理生命周期。它的缺点是难以精细控制事件回调设置OnFinish比较麻烦。所以我的原则是简单临时效果用TAnimator涉及状态联动和事件序列的老老实实放置命名好的动画组件。两条路各有适用场景不要混用。4. 常见问题与排查技巧实录4.1 动画不生效、动画闪跳的排查路径很多初学者遇到“动画组件拖上去了但运行时没有任何反应”。我总结了几条高频原因按检查优先级排序Properties属性名写错。这是最高频的坑。FMX属性名严格区分大小写且必须与运行时属性名完全一致。例如透明度属性是Opacity不是opacity位置属性是Position.X不是Position.X颜色属性是Fill.Color不是Color。写错时编译并不会报错但运行时动画组件找不到属性直接失效。动画组件的Enabled为False。有些动画组件在设计器默认Enabled是True但也经常因为多次复制粘贴操作导致Enabled被置为False。检查GroupIndex和事件干扰也在这里。如果多个动画组件共享一个GroupIndex那么GroupIndex相同的动画组件会互斥每次只能有一个播放。属性被布局系统覆盖。前面反复强调的AlignClient问题是最常见的“动画闪跳”根源动画明明播放了但控件坐标又被Align强制拉回。解决方法是额外嵌套容器或者动画期间临时把Align设为None。窗体的Opacity0但动画没触发。如果动画组件设了TriggerIsVisible并且TriggerTarget指向窗体本身但窗体的Visible属性在创建时已是TrueTrigger只在状态变化时触发一次启动时不会有反应。这个时候需要手动Start或者用FormShow事件里调用动画组件的Start。我写了一个简短的自检清单出现问题按顺序排查排查项检查方法常见坑PropertyName属性名对照对象监视器里的实际属性名大小写错误、缺少父路径Enabled和Trigger确认动画组件Enabled是TrueTrigger类型和TriggerTarget匹配Trigger在不需要时干扰动画Align与布局检查动画目标控件是否被Align控制AlignClient时Position动画无效Duration和插值确认Duration大于0插值类型不是Out/In为ExactlyNoneDuration为0导致瞬间跳变Owner生命周期确认动画组件的Owner是目标控件本身Owner释放后产生空引用4.2 读FMX动画源码的几个高价值入口Delphi 12Delphi 12发布后FMX源码可以直接打开这是排查问题和学习动画底层原理的绝佳入口。我经常看的几个文件都在安装目录下的Source\fmx里。比如FMX.Types.pas里面定义了TFmxObject和属性系统的基础架构动画组件本质就是通过这个属性系统动态修改控件属性的。理解它你就明白为什么PropertyName写错不报错。再看FMX.Ani.pas定义了TAnimation、TFloatAnimation、TColorAnimation这些类。我在里面看到过TAnimator的实现它每次调用静态方法时会创建一个临时的TAnimation对象播放结束后释放。这解释了一个问题TAnimator无法获取动画组件的持续引用想中途停止不太好操作。还有一个高价值入口是FMX.Objects.pas里的TShape、TRectangle的Fill属性定义。你会在里面看到Fill.Kind可以设置成None、Solid、Gradient、Bitmap等。ColorAnimation的目标Fill.Color只在Fill.KindSolid时有效如果你把Fill.Kind设为Gradient或BitmapColorAnimation会失效。这个细节文档里很少写源码里一眼就能看到。源码阅读的方法上我的建议是从一个简单动画组件开始比如HFloatAnimation在它的Start方法上下断点观察它怎么创建插值器、怎么驱动目标属性。跟着源码走一遍很多玄学问题都会豁然开朗。4.3 性能优化透明窗体与子窗体动画的坑动画卡顿不一定是因为帧率低更常见的是“界面重绘面积过大”。FMX渲染性能最怕大面积的渐变和透明混合尤其在跨平台环境。透明窗体上做全窗体级别的Opacity动画等于每一帧都要把整个窗体的内容与桌面背景重新混合这是一个相当昂贵的操作。如果窗体里又有大量文字和控件这个成本还会翻倍。我的优化思路是避免全窗体动画尽量把动画限制在某个局部容器里。例如想让通知栏淡入淡出可以只对通知栏所在的Rectangle做动画而不是整个窗体。Panel顶替子窗体做页面切换时也要注意旧的Panel如果还Visible并且Opacity还没降为0它会和新的Panel叠在一起增加一次半透明混合。我在项目里特别要求切换动画结束后立刻把旧Panel的Visible设为False避免后续无谓的混合开销。另一个常被忽视的性能点是内存位图的缓存。如果动画中涉及尺寸变化FMX需要连续重新布局和栅格化。Scale动画如果发生在包含大量文字的Panel上文字会逐帧重新栅格化导致肉眼可见的模糊闪动。解决方案有两个一是动画期间给Panel设置一个截图纹理通过TRenderTarget或Canvas的ToBitmap作为静态图片动画结束后再恢复真实控件二是尽量少对带文字的复杂容器做Scale动画改Position动画因为位置变化不涉及重栅格化性能开销小很多。4.4 动画与布局属性冲突的避坑清单动画并不是独立存在的它会影响控件的布局状态反过来布局状态也会破坏动画。这一节把我积攒的冲突案例整理成清单当作最后一道防线。第一条动画期间尽量不要让用户调整窗体大小。窗体的Align布局在窗体尺寸改变时会对所有子控件重新布局这会把正在动画中的Position和Size全部打回原样。如果用户有拖拽缩放宽高的需求最稳妥的方案是在动画播放期间暂时禁用窗体边框的Resize或者动画完成后用代码重新同步一次目标值。第二条ControlType与Scale动画的关系。FMX控件有两种ControlTypePlatform和Styled。Platform类型在Windows下会创建原生Win32控件句柄当你对该控件做Scale动画时原生窗口的尺寸通常不会实时跟随FMX的视觉变换经常出现视觉位置和实际点击区域不一致。解决的办法是使用ControlTypeStyled让FMX自己绘制所有内容这样Scale变换对点击区域同样生效。做窗体级动画尤其是缩放动画时尽量全部使用Styled控件。第三条颜色动画对控件的状态颜色有影响。比如TButton有文本颜色、背景色、边框色等多个属性本身由Style中的触发器管理。当你用ColorAnimation对Button的TextColor做动画时如果Button状态Normal/Hover/Pressed发生变化Style会强写回颜色动画就会被强行终止。所以对Styled控件做颜色动画最好在Style的Normal状态禁用Style自身的颜色更新或者把动画应用在自定义Rectangle上而不是标准控件上。第四条尽量把动画组件放在目标控件的子节点下。在设计器里很多人习惯把动画组件都拖到窗体上这样所有动画平级排列管理起来不直观。更好的做法是把动画组件拖放到其目标控件的下层级例如要动画Panel1的透明度就把FloatAnimation放进Panel1的子节点。这样做的好处是Panel1销毁时动画组件自动释放且RBTree遍历顺序清晰不容易跟其他动画冲突。最后再分享一个小技巧这个技巧帮我排查过无数次问题FMX设计器里动画组件运行时如果不希望它出现在Structure面板把它的DesignVisible设为False即可。这样界面结构更清爽动画组件少时也许无所谓但动画一多这个习惯能大大提升维护效率。我在实际使用中还有一个体会动画效果是做给用户看的而不是做给自己看的。参数上再完美如果动画拖慢了操作节奏用户照样会烦。所以每次做完动画我都建议用一个普通配置的机器跑一遍真实业务路径确认从点击到结果出来的总时间没有因为动画而显著增加。0.3到0.5秒的动画最安全超过了这个区间就要警惕它是否已经影响到了生产操作。现在FMX把动画的门槛压得足够低真正拉开差距的其实是动画的取舍和节奏控制。愿你也能在你的窗体上做出那种刚刚好的动态效果。
返回列表