如何扩展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
相关产品推荐
相关产品推荐

