-
Notifications
You must be signed in to change notification settings - Fork 361
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/296-pinch-and-zoom #297
base: main
Are you sure you want to change the base?
Changes from all commits
0a86788
fe9ab42
00782b9
c9f8fde
5ea7ae9
5725af3
72e41df
ede48dd
2d6f4e5
e0f1c37
257e712
9a570c7
695537c
744dda3
59659ab
db4d0e7
5d932b1
f73549d
223b2a7
f64c4c2
52dd9de
583dd54
f9d9e6a
3eddd6c
684c92c
5fd878f
1ad1196
0819d5d
f6fd13a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,8 @@ namespace Microcharts.Forms | |
using Xamarin.Forms; | ||
using SkiaSharp.Views.Forms; | ||
using SkiaSharp; | ||
using System; | ||
using Xamarin.Forms.Internals; | ||
|
||
public class ChartView : SKCanvasView | ||
{ | ||
|
@@ -17,6 +19,8 @@ public ChartView() | |
this.PaintSurface += OnPaintCanvas; | ||
} | ||
|
||
public event EventHandler<SKPaintSurfaceEventArgs> ChartPainted; | ||
|
||
#endregion | ||
|
||
#region Static fields | ||
|
@@ -60,6 +64,15 @@ private static void OnChartChanged(BindableObject d, object oldValue, object val | |
|
||
if (view.chart != null) | ||
{ | ||
AxisBasedChart axisChart = view.chart as AxisBasedChart; | ||
if( axisChart != null && axisChart.EnableZoom ) | ||
{ | ||
//FIXME: how to handle disable zoom after already enabled | ||
var pinchGesture = new PinchGestureRecognizer(); | ||
pinchGesture.PinchUpdated += view.OnPinchUpdated; | ||
view.GestureRecognizers.Add(pinchGesture); | ||
} | ||
|
||
view.handler = view.chart.ObserveInvalidate(view, (v) => v.InvalidateSurface()); | ||
} | ||
} | ||
|
@@ -74,6 +87,81 @@ private void OnPaintCanvas(object sender, SKPaintSurfaceEventArgs e) | |
{ | ||
e.Surface.Canvas.Clear(SKColors.Transparent); | ||
} | ||
|
||
ChartPainted?.Invoke(sender, e); | ||
} | ||
|
||
|
||
float zoomCurScale = 1; | ||
float zoomStartScale = 1; | ||
|
||
SKPoint zoomStartOrigin = new SKPoint(0, 0); | ||
SKPoint zoomStartOffset = new SKPoint(0, 0); | ||
SKPoint zoomTranslation = new SKPoint(0, 0); | ||
|
||
void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e) | ||
{ | ||
AxisBasedChart axisChart = this.Chart as AxisBasedChart; | ||
if (axisChart == null) return; | ||
|
||
if (e.Status == GestureStatus.Started) | ||
{ | ||
// Store the current scale factor applied to the wrapped user interface element, | ||
// and zero the components for the center point of the translate transform. | ||
zoomStartScale = zoomCurScale = axisChart.XForm.Scale; | ||
zoomStartOrigin = new SKPoint( (float)e.ScaleOrigin.X, (float)e.ScaleOrigin.Y); | ||
zoomStartOffset = axisChart.XForm.Offset; | ||
} | ||
|
||
if (e.Status == GestureStatus.Running) | ||
{ | ||
float eScale = (float)e.Scale; | ||
SKPoint eScaleOrigin = new SKPoint((float)e.ScaleOrigin.X, (float)e.ScaleOrigin.Y); | ||
SKPoint canvasSize = new SKPoint(CanvasSize.Width, CanvasSize.Height); | ||
|
||
// e.Scale is the delta to be applied for the current frame | ||
// Calculate the scale factor to be applied. | ||
zoomCurScale += (eScale - 1) * zoomStartScale; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't looked into this too much, but this seems overcomplicated. |
||
zoomCurScale = Math.Max(1, zoomCurScale); | ||
zoomCurScale = Math.Min(5, zoomCurScale); | ||
|
||
SKPoint zoomPanDelta = new SKPoint((eScaleOrigin.X - zoomStartOrigin.X) * CanvasSize.Width, (eScaleOrigin.Y - zoomStartOrigin.Y) * CanvasSize.Height); | ||
|
||
// The ScaleOrigin is in relative coordinates to the wrapped user interface element, | ||
// so get the X pixel coordinate. | ||
float renderedX = (float)X + zoomStartOffset.X; | ||
float deltaX = renderedX / CanvasSize.Width; | ||
float deltaWidth = CanvasSize.Width / (CanvasSize.Width * zoomStartScale); | ||
float originX = (eScaleOrigin.X - deltaX) * deltaWidth; | ||
|
||
//Console.WriteLine("ScaleOrigin: {0}, {1}", e.ScaleOrigin.X, e.ScaleOrigin.Y); | ||
|
||
// The ScaleOrigin is in relative coordinates to the wrapped user interface element, | ||
// so get the Y pixel coordinate. | ||
float renderedY = (float)Y + zoomStartOffset.Y; | ||
float deltaY = renderedY / CanvasSize.Height; | ||
float deltaHeight = CanvasSize.Height / (CanvasSize.Height * zoomStartScale); | ||
float originY = (eScaleOrigin.Y - deltaY) * deltaHeight; | ||
|
||
// Calculate the transformed element pixel coordinates. | ||
zoomTranslation.X = zoomStartOffset.X - (originX * CanvasSize.Width) * (zoomCurScale - zoomStartScale); | ||
zoomTranslation.Y = zoomStartOffset.Y - (originY * CanvasSize.Height) * (zoomCurScale - zoomStartScale); | ||
|
||
// Calculate final translation with pan, and clamp the whole thing | ||
SKPoint final = zoomTranslation + zoomPanDelta; | ||
final.X = Math.Min(Math.Max(final.X, -CanvasSize.Width * (zoomCurScale - 1)), 0); | ||
final.Y = Math.Min(Math.Max(final.Y, -CanvasSize.Height * (zoomCurScale - 1)), 0); | ||
|
||
axisChart.XForm.Scale = (float)zoomCurScale; | ||
axisChart.XForm.Offset = final; | ||
InvalidateSurface(); | ||
|
||
} | ||
|
||
if (e.Status == GestureStatus.Completed) | ||
{ | ||
InvalidateSurface(); | ||
} | ||
} | ||
|
||
#endregion | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this should be moved into the AxisBasedChart and then have the ChartView call an init function on the chart.