如何在 Windows 上为 Delphi VCL 表单的调整大小设置动画?

如何解决如何在 Windows 上为 Delphi VCL 表单的调整大小设置动画?

是否有任何相当简单和健壮的方法来平滑动画在 Windows 上以编程方式调整 Delphi VCL 表单的大小?

例如,当用户单击“显示详细信息”按钮时,表单的高度会增加,并在新客户区中显示详细信息面板。

通过设置 Height(或 ClientHeight)属性来调整表单的大小会立即调整它的大小。我希望表单的高度在半秒内从其原始值平滑增长到新值。

如何平滑地为 Delphi VCL 表单的大小调整设置动画?

解决方法

是的,这其实很简单。

可能最简单的方法是将解决方案基于 TTimer,它每秒触发大约 30 次,每次更新表单的大小。

我们只需要从时间到尺寸(宽度或高度)的映射 T,这样 T(0) 是原始尺寸, T(1) 是最终的目标大小,T(t) 是 t 时刻的中间大小,归一化到 [0,1]。

这里最简单的方法是让大小随时间线性增长或缩小。然而,这看起来很糟糕。相反,我们应该使用一些 sigmoid function 使速度在开始和结束时变慢,并在 t = 0.5 时达到最大值。我最喜欢的 sigmoid 函数是 inverse tangent function,但我们同样可以使用 hyperbolic tangent functionerror function

现在,如果 FFrames[i] 是第 i 帧的大小,则

var F := 1 / ArcTan(Gamma);

for var i := 0 to High(FFrames) do
begin
  var t := i / High(FFrames);         // [0,1]
      t := 2*t - 1;                   // [-1,1]
      t := F*ArcTan(Gamma*t);         // sigmoid transformation
      t := (t + 1) / 2;               // [0,1]
  FFrames[i] := Round((1 - t) * AFrom + t * ATo);
end;

根据这个方案计算轨迹。请注意,FFrames[i] 是初始和最终大小的 convex combination

以下组件使用此代码来实现动画调整大小:

unit WindowAnimator;

interface

uses
  SysUtils,Windows,Types,Classes,Vcl.Forms,Vcl.ExtCtrls;

type
  TWindowAnimator = class(TComponent)
  strict private
  type
    TAxis = (axWidth,axHeight);
  const
    DEFAULT_GAMMA = 10;
    DEFAULT_DURATION = 1000 {ms};
    FrameCount = 256;
  var
    FTimer: TTimer;
    FGamma: Integer;
    FDuration: Integer {ms};
    FFrames: array[0..FrameCount - 1] of Integer;
    FAxis: TAxis;
    FTarget: Integer;
    FAnimStart,FAnimEnd: TDateTime;
    FForm: TCustomForm;
    FBeforeProc,FAfterProc: TProc;
    procedure TimerProc(Sender: TObject);
    procedure Plot(AFrom,ATo: Integer);
    procedure Stop;
    procedure Animate(ABeforeProc: TProc = nil; AAfterProc: TProc = nil);
    procedure DoBegin;
    procedure DoFinish;
  public
    constructor Create(AOwner: TComponent); override;
    procedure AnimateWidth(ANewWidth: Integer; ABeforeProc: TProc = nil; AAfterProc: TProc = nil);
    procedure AnimateHeight(ANewHeight: Integer; ABeforeProc: TProc = nil; AAfterProc: TProc = nil);
  published
    property Gamma: Integer read FGamma write FGamma default DEFAULT_GAMMA;
    property Duration {ms}: Integer read FDuration write FDuration default DEFAULT_DURATION;
  end;

procedure Register;

implementation

uses
  Math,DateUtils;

procedure Register;
begin
  RegisterComponents('Rejbrand 2020',[TWindowAnimator]);
end;

{ TWindowAnimator }

procedure TWindowAnimator.Animate(ABeforeProc,AAfterProc: TProc);
begin

  if FForm = nil then
    Exit;

  FBeforeProc := ABeforeProc;
  FAfterProc := AAfterProc;

  DoBegin;
  FAnimStart := Now;
  FAnimEnd := IncMilliSecond(FAnimStart,FDuration);
  FTimer.Enabled := True;

end;

procedure TWindowAnimator.AnimateHeight(ANewHeight: Integer;
  ABeforeProc,AAfterProc: TProc);
begin

  if FForm = nil then
    Exit;

  Stop;
  FAxis := axHeight;
  Plot(FForm.Height,ANewHeight);
  Animate(ABeforeProc,AAfterProc);

end;

procedure TWindowAnimator.AnimateWidth(ANewWidth: Integer;
  ABeforeProc,AAfterProc: TProc);
begin

  if FForm = nil then
    Exit;

  Stop;
  FAxis := axWidth;
  Plot(FForm.Width,ANewWidth);
  Animate(ABeforeProc,AAfterProc);

end;

constructor TWindowAnimator.Create(AOwner: TComponent);
begin
  inherited;
  if AOwner is TCustomForm then
    FForm := TCustomForm(AOwner);
  FGamma := DEFAULT_GAMMA;
  FDuration := DEFAULT_DURATION;
  FTimer := TTimer.Create(Self);
  FTimer.Interval := 30;
  FTimer.OnTimer := TimerProc;
  FTimer.Enabled := False;
end;

procedure TWindowAnimator.DoBegin;
begin
  if Assigned(FBeforeProc) then
    FBeforeProc();
end;

procedure TWindowAnimator.DoFinish;
begin
  if Assigned(FAfterProc) then
    FAfterProc();
end;

procedure TWindowAnimator.Plot(AFrom,ATo: Integer);
begin

  FTarget := ATo;

  var F := 1 / ArcTan(Gamma);

  for var i := 0 to High(FFrames) do
  begin
    var t := i / High(FFrames);         // [0,1]
        t := 2*t - 1;                   // [-1,1]
        t := F*ArcTan(Gamma*t);         // sigmoid transformation
        t := (t + 1) / 2;               // [0,1]
    FFrames[i] := Round((1 - t) * AFrom + t * ATo);
  end;

end;

procedure TWindowAnimator.Stop;
begin
  FTimer.Enabled := False;
end;

procedure TWindowAnimator.TimerProc(Sender: TObject);
begin

  var LNow := Now;

  if (FForm = nil) or (FAnimEnd = 0.0) then
  begin
    FTimer.Enabled := False;
    Exit;
  end;

  if LNow > FAnimEnd then // play it safe
  begin
    FTimer.Enabled := False;
    case FAxis of
      axWidth:
        FForm.Width := FTarget;
      axHeight:
        FForm.Height := FTarget;
    end;
    DoFinish;
    Exit;
  end;

  var t := MilliSecondsBetween(LNow,FAnimStart) / MilliSecondsBetween(FAnimStart,FAnimEnd);
  var i := EnsureRange(Round(t * High(FFrames)),High(FFrames));

  case FAxis of
    axWidth:
      FForm.Width := FFrames[i];
    axHeight:
      FForm.Height := FFrames[i];
  end;

end;

end.

要使用此组件,只需将其放在表单上并使用其公共方法:

procedure AnimateWidth(ANewWidth: Integer; ABeforeProc: TProc = nil;
  AAfterProc: TProc = nil);
procedure AnimateHeight(ANewHeight: Integer; ABeforeProc: TProc = nil;
  AAfterProc: TProc = nil);

可选的 TProc 引用让您可以在动画之前和/或之后运行一些代码;通常,您希望在增大大小后填充任何新获得的客户区,并在减小大小前隐藏一些内容。

这是正在运行的组件,显示和隐藏“详细信息”文本:

Screen recording

这是一个更复杂的例子,包含一个三级输入过程:

Screen recording

动画的总持续时间以及 sigmoid 函数的锐度,可以使用组件的已发布属性进行调整。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)> insert overwrite table dwd_trade_cart_add_inc > select data.id, > data.user_id, > data.course_id, > date_format(
错误1 hive (edu)> insert into huanhuan values(1,'haoge'); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive> show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 <configuration> <property> <name>yarn.nodemanager.res