1

I have a TScrollbox on my form. Placed onto the Scrollbox is a TLayout that is wider and taller than the device viewport so the horiz and vert scrollbars show and the user can manually move the layout.

I also have a gesture setup so that the user can longpress the Scrollbox and get the layout back to origin (0,0).

procedure TfrmMain.ScrollBox1Gesture(Sender: TObject;
  const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
  if EventInfo.GestureID = System.UITypes.igiLongTap then
  begin
     ScrollBox1.ViewportPosition := PointF(0, 0);
     ScrollBox1.RealignContent;
  end;

Now this works great, but as it happens rather quickly. I thought perhaps I could use:

TAnimator.AnimateFloat(Scrollbox1, 'ScrollBox1.ViewportPosition.X', 0, 0.3);

to make the movement from current position back to 0 a bit more gentle, but of course, that's never going to work because you can't assign a value to a PointF.X or PointF.Y directly (and therefore neither can the animator).

So how can it be done? Thanks

Freddie Bell
  • 2,186
  • 24
  • 43
  • 1
    Subclass `TScrollBox` and implement a `ViewPortPositionX `property with a getter and a setter method. – LU RD May 09 '16 at 13:08
  • Thanks, but I just tried to start doing that and realised how little I know about class helpers and inheriting and overriding protect methods that return values. – Freddie Bell May 09 '16 at 13:28
  • See [Delphi subclass visual component and use it](http://stackoverflow.com/a/14783549/576719) for inspiration. – LU RD May 09 '16 at 13:33
  • I'm going to look at a different solution thanks. This one seems rather like the long way around an apparently simple problem. – Freddie Bell May 09 '16 at 13:39
  • ie. It might be betterto subclass a TAnimation and create a TAnimateFloatF – Freddie Bell May 09 '16 at 13:51
  • I would rather create a `TPointAnimation` derived from `TCustomPropertyAnimation` setting `ViewportPosition` in one go inside. This has the advantage of scrolling vertical and horizontal simultaneously. – Uwe Raabe May 09 '16 at 14:19

1 Answers1

3

As a built-in PointAnimation is missing, here is a self written one.

type
  TPointAnimation = class(TCustomPropertyAnimation)
  private
    FStartFloat: TPointF;
    FStartFromCurrent: Boolean;
    FStopFloat: TPointF;
  protected
    procedure FirstFrame; override;
    procedure ProcessAnimation; override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property AnimationType default TAnimationType.In;
    property AutoReverse default False;
    property Delay;
    property Duration nodefault;
    property Enabled default False;
    property Interpolation default TInterpolationType.Linear;
    property Inverse default False;
    property Loop default False;
    property OnFinish;
    property OnProcess;
    property PropertyName;
    property StartFromCurrent: Boolean read FStartFromCurrent write
        FStartFromCurrent default False;
    property StartValue: TPointF read FStartFloat write FStartFloat stored True;
    property StopValue: TPointF read FStopFloat write FStopFloat stored True;
    property Trigger;
    property TriggerInverse;
  end;

constructor TPointAnimation.Create(AOwner: TComponent);
begin
  inherited;
  Duration := 0.2;
  FStartFloat := PointF(0, 0);
  FStopFloat := PointF(0, 0);
end;

procedure TPointAnimation.FirstFrame;
var
  T: TRttiType;
  P: TRttiProperty;
begin
  if StartFromCurrent then
  begin
    T := SharedContext.GetType(FInstance.ClassInfo);
    if T <> nil then
    begin
      P := T.GetProperty(FPath);
      if (P <> nil) and (P.PropertyType.TypeKind = tkRecord) then
        StartValue := P.GetValue(FInstance).AsType<TPointF>;
    end;
  end;
end;

procedure TPointAnimation.ProcessAnimation;
var
  newPoint: TPointF;
  T: TRttiType;
  P: TRttiProperty;
begin
  if FInstance <> nil then
  begin
    T := SharedContext.GetType(FInstance.ClassInfo);
    if T <> nil then
    begin
      P := T.GetProperty(FPath);
      if (P <> nil) and (P.PropertyType.TypeKind = tkRecord) then begin
        newPoint := PointF(InterpolateSingle(FStartFloat.X, FStopFloat.X, NormalizedTime),
                           InterpolateSingle(FStartFloat.Y, FStopFloat.Y, NormalizedTime));
        P.SetValue(FInstance, TValue.From<TPointF>(newPoint));
      end;
    end;
  end;
end;

Declare a field inside the forms private section

ani: TPointAnimation;

Create an instance during FormCreate

ani := TPointAnimation.Create(Self);
ani.PropertyName := 'ViewportPosition';
ani.StartFromCurrent := true;
ani.Duration := 0.3;
ScrollBox1.AddObject(ani);

and start it when needed

ani.Start;
Uwe Raabe
  • 45,288
  • 3
  • 82
  • 130