一步一步学习使用FireMonkey动画(1) 使用动画组件为窗体添加动态效果

FireMonkey提供了一系列的动画控件,为基于FMX(FireMonkey的简称)的应用程序开发提供了较大的灵活性。在VCL上面创建动画,需要开发人员用一个TTimer组件,然后不断的移动目标物体的位置,使得目标对象看起来像是动了一样,这需要编写大量的控制代码,而且效果并不是特别理想。

本课将介绍如下的内容:

  1. 使用传统TTimer创建动画。
  2. 使用TFloatAnimation通过改变图片的位置创建动画。
  3. 使用TPathAnimation创建路径动画。

本课是动画系列的入门篇,在稍后的课时中,会详细介绍这些动画组件的具体参数和使用方法。

1. 使用TTimer创建动画

下面通过一个简单的例子来演示这种做法:

1. 单击主菜单中的 File > New > Multi-Device Application - Delphi > Blank Application ,创建一个新的多设备应用程序。

建议立即单击工具栏上的Save All按钮,将单元文件保存为Form.Main.pas,将项目保存为Ani_Base.dproj。

项目结构应该像这样:

2. 在工具面板[Palette]中,添加TTimer组件,TImage组件和一个TButton组件。

选中TImage组件,在Object Inspector面板上找到MultiResBitmap属性,单击右侧的按钮打开Editing Image1.MultiResBitmap对话框,在弹出的对话框中添加一幅图片,类似下图:

请继续在Object Inspector(对象检查器或属性面板)上设置这个图片的Position.X=0;Position.Y=200。

选中按钮组件,将其Text设置为"TTimer方法"。

3. 接下来,在FormCreate事件中初始化了TTimer控件,然后为TTimer控件的OnTimer事件添加了动画的代码,并且在按钮的单击事件中启用了TTimer控件,3个事件处理代码如下:

Pascal 复制代码
procedure TfrmMain.Button1Click(Sender: TObject);
begin
// 切换Timer1的启用状态:当Timer1当前为启用时则禁用,当前为禁用时则启用
Timer1.Enabled := not Timer1.Enabled;
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
// 设置定时器间隔为100毫秒(即每0.1秒触发一次)
Timer1.Interval := 100;
// 指定定时器的触发事件处理程序为Timer1Timer
Timer1.OnTimer := Timer1Timer;
// 初始化时禁用定时器
Timer1.Enabled := False;
end;

procedure TfrmMain.Timer1Timer(Sender: TObject);
begin
{ 按菱形路径顺时针移动 }
with Image1.Position do
begin
{ 第一阶段:从左下到顶部 }
if (X < 200) and (Y <= 200) then
begin
X := X + 10; // X坐标右移10像素
Y := Y - 10; // Y坐标上移10像素
end
{ 第二阶段:从顶部到右下 }
else if (X >= 200) and (Y < 200) then
begin
X := X + 10; // X坐标右移10像素
Y := Y + 10; // Y坐标下移10像素
end
{ 第三阶段:从右下到底部 }
else if (X > 200) and (Y >= 200) then
begin
X := X - 10; // X坐标左移10像素
Y := Y + 10; // Y坐标下移10像素
end
{ 第四阶段:从底部回退到左下 }
else
begin
X := X - 10; // X坐标左移10像素
Y := Y - 10; // Y坐标上移10像素
end;
end;
end;

通过四个阶段的路径,模拟了图片的菱形动画路径,运行效果如下所示:

现在图片果然已经按菱形动画路径在跳动了。

2. 使用TFloatAnimation创建动画

3. 在工具面板[Palette]中,添加一个新的TButton组件,指定其Text属性为"使用TFloatAnimation组件"

接下来选中TImage组件,依次拉3个TFloatAnimation组件到它的下面,也就是TFloatAnimation以TImage组件作为Parent。Structure结构视图如下所示:

注意:动画组件并没有可视化图标,需要在Structure上进行查看和选择

在这里可以直接在对象检查器面板设置这3个TFloatAnimation组件的属性。由于动画组件并不会在表单上有一个特定的图标显示,需要开发人员在Structure面板上选中动画,然后在对象检查器上设置属性。在这里将使用代码方式列出参数值,以便于设置。

4. 为Button2添加如下的代码,初始化并且运行动画组件。

Pascal 复制代码
procedure TfrmMain.Button2Click(Sender: TObject);
begin
// 配置X轴平移动画 (FloatAnimation1)
FloatAnimation1.PropertyName := 'Position.X'; // 设置动画目标属性为X坐标
FloatAnimation1.StartFromCurrent := True; // 从当前位置开始动画
FloatAnimation1.StopValue := Image1.Position.X + 400; // 设置X轴终点值:当前位置向右400像素
FloatAnimation1.Duration := 4; // 设置动画持续时间为4秒
FloatAnimation1.Loop := True; // 设置动画循环播放
FloatAnimation1.AutoReverse := True; // 设置动画自动反向(往复运动)

// 配置Y轴向上移动动画 (FloatAnimation2) - 初始垂直移动
FloatAnimation2.PropertyName := 'Position.Y'; // 设置动画目标属性为Y坐标
FloatAnimation2.StartFromCurrent := True; // 从当前位置开始动画
FloatAnimation2.StopValue := Image1.Position.Y - 200; // 设置Y轴终点值:当前位置向上200像素
FloatAnimation2.Duration := 2; // 设置动画持续时间为2秒

// 配置Y轴往复运动动画 (FloatAnimation3) - 后续垂直振荡
FloatAnimation3.PropertyName := 'Position.Y'; // 设置动画目标属性为Y坐标
FloatAnimation3.StartFromCurrent := True; // 从当前位置开始动画
FloatAnimation3.StopValue := Image1.Position.Y + 200; // 设置Y轴终点值:当前位置向下200像素
FloatAnimation3.Duration := 4; // 设置动画持续时间为4秒
FloatAnimation3.Loop := True; // 设置动画循环播放
FloatAnimation3.AutoReverse := True; // 设置动画自动反向(往复运动)

// 检查X轴动画是否已在运行,避免重复启动
if not FloatAnimation1.Running then
begin
FloatAnimation1.Start; // 启动X轴往复平移动画
FloatAnimation2.Start; // 启动Y轴向上移动动画
// 注意:FloatAnimation3不会立即启动,将在FloatAnimation2完成后由FloatAnimation2Finish事件触发
end;
end;

procedure TfrmMain.FloatAnimation2Finish(Sender: TObject);
begin
// FloatAnimation2动画完成时的回调事件
// 当向上移动动画完成后,启动垂直方向的往复振荡动画
FloatAnimation3.Start; // 启动Y轴往复运动动画(上下振荡)
end;

当用户点击 Button2 按钮时,Image1 会开始执行一系列动画:

  1. 立即开始:在 水平方向(X轴) 上进行持续的左右往复移动(像钟摆)。

  2. 立即开始:在 垂直方向(Y轴) 上先向上移动一段距离。

  3. 随后开始:待向上的移动完成后,在 垂直方向(Y轴) 上转变为持续的上下往复移动(像弹跳)。

  4. 最终,Image1 会一边左右摇摆,一边上下弹跳,形成一个非常动态的菱形动画视觉效果。

如果与TTimer生成的动画进行比较,可以看到这里的动画要顺滑很多。

3. 使用TPathAnimation创建动画

5. 选中Image1控件,然后从工具栏拖一个TPathAnimation控件到其上面,使之成为Image1的子组件。

然后添加一个按钮,其Text属性设置为"使用TPathAnimation组件",为其添加如下的事件处理代码:

Pascal 复制代码
// 主窗体中 Button3 的点击事件处理过程
procedure TfrmMain.Button3Click(Sender: TObject);
begin
  // 设置路径动画的路径
  // 首先将路径的起始点移动到坐标原点 (0, 0)
  PathAnimation1.Path.MoveTo(PointF(0, 0));
  
  // 从当前点绘制直线到坐标 (200, -200)
  PathAnimation1.Path.LineTo(PointF(200, -200));
  
  // 从当前点继续绘制直线到坐标 (400, 0)
  PathAnimation1.Path.LineTo(PointF(400, 0));
  
  // 从当前点继续绘制直线到坐标 (200, 200)
  PathAnimation1.Path.LineTo(PointF(200, 200));
  
  // 闭合路径:从当前点绘制直线回起始点,形成封闭图形
  PathAnimation1.Path.ClosePath;
  
  // 设置动画循环播放属性为 True,动画将无限循环
  PathAnimation1.Loop := True;
  
  // 设置动画完成一次完整路径所需的时间为 8 秒
  PathAnimation1.Duration := 8;

  // 启动路径动画
  PathAnimation1.Start;
end;

上面的代码首先构建了一条封闭的路径,然后TImage会沿着这条路径来移动位置,形成动画效果。

可以看到,它实现了类似前是面2种的动画效果。

总结

传统的TTimer模式的动画,需要程序员控制动画的具体位置,对于复杂的动画,往往需要编写大量的代码。由于所有逻辑和UI更新都在OnTimer事件中完成,如果计算复杂会直接阻塞UI。

FireMonkey(以后简称FMX)提供的动画组件具有如下的优势:

  • 可以在设计时进行动画的配置,动画间隔由FireMonkey的渲染管线和计时器统一管理,通常利用GPU进行平滑渲染。
  • 动画的进度由内部高精度时钟控制,与持续时间(Duration)严格绑定,帧率是自适应的。
  • 动画引擎在后台处理所有计算,并通过安全的方式更新UI,开发者无需关心线程问题。
  • 内置插值(Inverse, Circular, Elastic等)、自动反转、循环、路径跟随等高级功能。

FMX除了提供丰富的动画组件外,还在TFmxObject对象中整合了动画函数,使得FireMonkey可以创造出非常丰富的动画效果的软件。

接下来会通过几节内容详细介绍这些动画组件的具体使用方法。

相关推荐
Frank_HarmonyOS8 分钟前
Android MVVM(Model-View-ViewModel)架构
android·架构
新子y4 小时前
【操作记录】我的 MNN Android LLM 编译学习笔记记录(一)
android·学习·mnn
想想吴7 小时前
Android.bp 基础
android·安卓·android.bp
bianshaopeng12 小时前
Android studio gradle 下载不下来
ide·android studio
写点啥呢13 小时前
Android为ijkplayer设置音频发音类型usage
android·音视频·usage·mediaplayer·jikplayer
22:30Plane-Moon16 小时前
项目1总结其三(图片上传功能)
ide·spring boot·vue
coder_pig18 小时前
🤡 公司Android老项目升级踩坑小记
android·flutter·gradle
死就死在补习班19 小时前
Android系统源码分析Input - InputReader读取事件
android
死就死在补习班19 小时前
Android系统源码分析Input - InputChannel通信
android