210 lines
4.6 KiB
Plaintext
210 lines
4.6 KiB
Plaintext
unit WindowAnimator;
|
|
|
|
interface
|
|
|
|
uses
|
|
SysUtils, Windows, Types, Classes, Vcl.Forms, Vcl.ExtCtrls;
|
|
|
|
type
|
|
TWindowAnimator = class(TComponent)
|
|
strict private
|
|
type
|
|
TAxis = (axWidth, axHeight, axWidthLeft);
|
|
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 AnimateWidthLeft(ANewWidth: Integer; ABeforeProc: TProc = nil; AAfterProc: TProc = nil); // 왼쪽으로 크기 늘어나게 추가 22_0427 23:16:35 kku
|
|
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;
|
|
|
|
procedure TWindowAnimator.AnimateWidthLeft(ANewWidth: Integer; ABeforeProc: TProc = nil; AAfterProc: TProc = nil);
|
|
begin
|
|
if FForm = nil then
|
|
Exit;
|
|
|
|
Stop;
|
|
FAxis := axWidthLeft;
|
|
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);
|
|
var
|
|
nTemp: Integer;
|
|
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;
|
|
axWidthLeft:
|
|
begin
|
|
nTemp := FTarget - FForm.Width;
|
|
FForm.Width := FTarget;
|
|
FForm.Left := FForm.Left - nTemp;
|
|
end;
|
|
end;
|
|
DoFinish;
|
|
Exit;
|
|
end;
|
|
|
|
var t := MilliSecondsBetween(LNow, FAnimStart) / MilliSecondsBetween(FAnimStart, FAnimEnd);
|
|
var i := EnsureRange(Round(t * High(FFrames)), 0, High(FFrames));
|
|
|
|
case FAxis of
|
|
axWidth:
|
|
FForm.Width := FFrames[i];
|
|
axHeight:
|
|
FForm.Height := FFrames[i];
|
|
axWidthLeft:
|
|
begin
|
|
nTemp := FFrames[i] - FForm.Width;
|
|
FForm.Width := FFrames[i];
|
|
FForm.Left := FForm.Left - nTemp;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
end.
|