Zoom and Scroll 2D Charts in Mobile

Some time ago, we wrote a post about using Delphi Gestures with TeeChart. It covered 3D Charts and showed how we can use TGestureManager to modify ZoomFloat, ElevationFloat and RotationFloat properties of the TChart.

This technique still works perfectly nowadays and the same concept can be applied to 2D charts. We just need to modify the Axes scales instead of the Aspect.

As before, you just need to associate the TGestureManager to TChart’s Touch property. Then, enable Zoom, Pan and DoubleTap interactive gestures on it and implement of TChart‘s OnGesture event.

uses System.Math, FMXTee.Series;

procedure TForm1.FormCreate(Sender: TObject);
var Series1: TLineSeries;
begin
  Chart1.View3D:=False;
  Chart1.Legend.Hide;
  Chart1.Color:=clWhite;
  Chart1.Gradient.Visible:=False;
  Chart1.Walls.Back.Color:=clWhite;
  Chart1.Walls.Back.Gradient.Visible:=False;
  Chart1.Hover.Hide;

  Chart1.AllowZoom:=False;
  Chart1.AllowPanning:=pmNone;

  Series1:=TLineSeries(Chart1.AddSeries(TLineSeries));
  Series1.FillSampleValues;
end;

procedure TForm1.Chart1Gesture(Sender: TObject;
  const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
  if EventInfo.GestureID = igiZoom then
    handleZoom(EventInfo)
  else if EventInfo.GestureID = igiPan then
    handlePan(EventInfo)
  else if EventInfo.GestureID = igiDoubleTap then
    handleDoubleTap(EventInfo);

  Handled:=True;
end;

procedure TForm1.handleZoom(EventInfo: TGestureEventInfo);
var
  LObj: IControl;
  chart: TChart;
  diff: Double;
  xZoom, yZoom: Double;
begin
  LObj:=Self.ObjectAtPoint(ClientToScreen(EventInfo.Location));
  if LObj is TChart then
  begin
    if not(TInteractiveGestureFlag.gfBegin in EventInfo.Flags) then
    begin
      chart:=TChart(LObj.GetObject);
      chart.Title.Text.Text:=IntToStr(EventInfo.Distance);
      diff:=(EventInfo.Distance - FLastDIstance);
      XZoom:=chart.Axes.Bottom.CalcPosPoint(0) - chart.Axes.Bottom.CalcPosPoint(Round(Cos(EventInfo.Angle)*diff));
      YZoom:=chart.Axes.Left.CalcPosPoint(0) - chart.Axes.Left.CalcPosPoint(Round(Sin(EventInfo.Angle)*diff));
      chart.Axes.Bottom.SetMinMax(chart.Axes.Bottom.Minimum-XZoom, chart.Axes.Bottom.Maximum+XZoom);
      chart.Axes.Left.SetMinMax(chart.Axes.Left.Minimum-YZoom, chart.Axes.Left.Maximum+YZoom);
    end;
  end;
  FLastDIstance:=EventInfo.Distance;
end;

procedure TForm1.handlePan(eventInfo: TGestureEventInfo);
var
  LObj: IControl;
  chart: TChart;
  XDiff, YDiff: Double;
begin
  LObj:=Self.ObjectAtPoint(ClientToScreen(EventInfo.Location));
  if LObj is TChart then
  begin
    if not(TInteractiveGestureFlag.gfBegin in EventInfo.Flags) then
    begin
      chart:=TChart(LObj.GetObject);
      XDiff:=chart.Axes.Bottom.CalcPosPoint(Round(FLastPosition.X)) - chart.Axes.Bottom.CalcPosPoint(Round(EventInfo.Location.X));
      chart.Axes.Bottom.SetMinMax(chart.Axes.Bottom.Minimum+XDiff, chart.Axes.Bottom.Maximum+XDiff);
      YDiff:=chart.Axes.Left.CalcPosPoint(Round(FLastPosition.Y)) - chart.Axes.Left.CalcPosPoint(Round(EventInfo.Location.Y));
      chart.Axes.Left.SetMinMax(chart.Axes.Left.Minimum+YDiff, chart.Axes.Left.Maximum+YDiff);
    end;

    FLastPosition:=EventInfo.Location;
  end;
end;

procedure TForm1.handleDoubleTap(eventInfo: TGestureEventInfo);
var
  LObj: IControl;
begin
  LObj:=Self.ObjectAtPoint(ClientToScreen(EventInfo.Location));
  if LObj is TChart then
    ResetChart(TChart(LObj.GetObject));
end;

procedure TForm1.ResetChart(chart: TChart);
begin
  chart.Axes.Bottom.Automatic:=True;
  chart.Axes.Left.Automatic:=True;
end;

Yeray Alonso

Developer and coffee master at Steema Software