You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何扩展Delphi FMX中TAnimation的TInterpolationType支持自定义插值算法?

扩展Delphi FMX TAnimation的自定义插值算法方案

针对你的需求,以下几种实现方案各有优劣,可根据项目规模和扩展性需求选择:

方案1:策略模式重构Interpolation属性(最优解耦方案)

将原有枚举类型替换为接口类型,让插值算法成为可注入的独立组件,彻底解耦核心动画逻辑与算法实现。

首先定义插值算法接口及内置实现:

type
  IInterpolation = interface
    ['{00000000-0000-0000-0000-000000000001}'] // 替换为你自己的GUID
    function Calculate(const Progress: Single): Single;
  end;

  // 原有线性插值封装
  TLinearInterpolation = class(TInterfacedObject, IInterpolation)
  public
    function Calculate(const Progress: Single): Single;
  end;

  // 原有二次插值封装(其他内置算法同理)
  TQuadraticInterpolation = class(TInterfacedObject, IInterpolation)
  public
    function Calculate(const Progress: Single): Single;
  end;

function TLinearInterpolation.Calculate(const Progress: Single): Single;
begin
  Result := Progress;
end;

function TQuadraticInterpolation.Calculate(const Progress: Single): Single;
begin
  Result := Progress * Progress;
end;

修改TAnimation类的属性定义:

type
  TAnimation = class(TBaseAnimation) // 根据实际父类调整
  private
    FInterpolation: IInterpolation;
    procedure SetInterpolation(const Value: IInterpolation);
  public
    constructor Create(AOwner: TComponent); override;
    property Interpolation: IInterpolation read FInterpolation write SetInterpolation;
  protected
    function GetInterpolatedValue(const Start, EndValue, Progress: Single): Single; override;
  end;

constructor TAnimation.Create(AOwner: TComponent);
begin
  inherited;
  // 默认使用线性插值
  FInterpolation := TLinearInterpolation.Create;
end;

procedure TAnimation.SetInterpolation(const Value: IInterpolation);
begin
  if Assigned(Value) then
    FInterpolation := Value;
end;

function TAnimation.GetInterpolatedValue(const Start, EndValue, Progress: Single): Single;
begin
  Result := Start + (EndValue - Start) * FInterpolation.Calculate(Progress);
end;

自定义算法只需实现接口:

// 加速插值算法
TAccelerateInterpolation = class(TInterfacedObject, IInterpolation)
public
  function Calculate(const Progress: Single): Single;
end;

function TAccelerateInterpolation.Calculate(const Progress: Single): Single;
begin
  Result := Progress * Progress; // 二次方加速
end;

// 使用示例
Animation1.Interpolation := TAccelerateInterpolation.Create;

优势:完全遵循开闭原则,新增算法无需修改TAnimation核心代码,算法可复用、可独立测试,适合中大型项目长期维护。

方案2:枚举扩展+接口兼容(兼顾旧代码)

如果需要保留原有枚举用法以兼容旧代码,可以新增Custom枚举项,同时添加自定义接口属性作为补充。

type
  // 原有枚举新增Custom项
  TInterpolationType = (Linear, Quadratic, Cubic, Quartic, Quintic, Sinusoidal, 
    Exponential, Circular, Elastic, Back, Bounce, Custom);

  IInterpolation = interface
    ['{00000000-0000-0000-0000-000000000001}']
    function Calculate(const Progress: Single): Single;
  end;

  TAnimation = class(TBaseAnimation)
  private
    FInterpolation: TInterpolationType;
    FCustomInterpolation: IInterpolation;
    function GetActiveInterpolation: IInterpolation;
    procedure SetInterpolation(const Value: TInterpolationType);
    procedure SetCustomInterpolation(const Value: IInterpolation);
  protected
    function GetInterpolatedValue(const Start, EndValue, Progress: Single): Single; override;
  public
    property Interpolation: TInterpolationType read FInterpolation write SetInterpolation default Linear;
    property CustomInterpolation: IInterpolation read FCustomInterpolation write SetCustomInterpolation;
  end;

function TAnimation.GetActiveInterpolation: IInterpolation;
begin
  case FInterpolation of
    Linear: Result := TLinearInterpolation.Create;
    Quadratic: Result := TQuadraticInterpolation.Create;
    // ... 映射所有原有枚举到对应接口类
    Custom:
      if Assigned(FCustomInterpolation) then
        Result := FCustomInterpolation
      else
        Result := TLinearInterpolation.Create; // 兜底线性插值
  end;
end;

function TAnimation.GetInterpolatedValue(const Start, EndValue, Progress: Single): Single;
var
  LInterpolation: IInterpolation;
begin
  LInterpolation := GetActiveInterpolation;
  Result := Start + (EndValue - Start) * LInterpolation.Calculate(Progress);
end;

优势:兼容原有代码的枚举使用方式,同时支持自定义算法,适合老项目的增量升级。

方案3:事件驱动(轻量快速实现)

如果需求简单、不需要长期扩展,事件驱动是最快捷的实现方式,只需新增枚举项和事件属性。

type
  // 新增Custom枚举项
  TInterpolationType = (Linear, Quadratic, ..., Custom);

  // 自定义插值事件类型
  TCustomInterpolationEvent = function(const Progress: Single): Single of object;

  TAnimation = class(TBaseAnimation)
  private
    FInterpolation: TInterpolationType;
    FOnCustomInterpolation: TCustomInterpolationEvent;
    function CalculateProgress(const Progress: Single): Single;
  protected
    function GetInterpolatedValue(const Start, EndValue, Progress: Single): Single; override;
  public
    property Interpolation: TInterpolationType read FInterpolation write FInterpolation default Linear;
    property OnCustomInterpolation: TCustomInterpolationEvent read FOnCustomInterpolation write FOnCustomInterpolation;
  end;

function TAnimation.CalculateProgress(const Progress: Single): Single;
begin
  case FInterpolation of
    Linear: Result := Progress;
    Quadratic: Result := Progress * Progress;
    // ... 原有算法实现
    Custom:
      if Assigned(FOnCustomInterpolation) then
        Result := FOnCustomInterpolation(Progress)
      else
        Result := Progress; // 兜底逻辑
  end;
end;

function TAnimation.GetInterpolatedValue(const Start, EndValue, Progress: Single): Single;
begin
  Result := Start + (EndValue - Start) * CalculateProgress(Progress);
end;

使用时直接绑定事件:

// 自定义加速算法
function TMainForm.AccelerateFunc(const Progress: Single): Single;
begin
  Result := Progress * Progress;
end;

// 自定义减速算法
function TMainForm.DecelerateFunc(const Progress: Single): Single;
begin
  Result := 1 - Sqrt(1 - Progress * Progress);
end;

// 绑定使用
Animation1.Interpolation := Custom;
Animation1.OnCustomInterpolation := AccelerateFunc;

优势:代码改动最小,快速实现需求,适合小型项目或临时需求。

内容的提问来源于stack exchange,提问作者zeus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 20:05:13