Created
June 15, 2018 19:36
-
-
Save felipebaltazar/73c15e6ffe1dd57ae7e09150308a71a2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Draw.Core.Extensions; | |
using Draw.Math.Models; | |
using SkiaSharp.Views.Forms; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Internals; | |
using Ext = Draw.Core.Extensions.NumbersExtensions; | |
namespace Draw.Application.DrawSpace | |
{ | |
public class Ambient2D : StackLayout | |
{ | |
#region Internals | |
internal SKCanvasView CanvasView; | |
internal bool KeepSelection; | |
internal bool IsPanning; | |
internal bool IsZooning; | |
internal float CanvasScale = 1; | |
internal float WidthScreen = 0f; | |
internal float HeightScreen = 0f; | |
internal float StartCanvasScale = 1; | |
internal float TouchSensibility = 3f; | |
internal float MaximumZoomStep = 1.3f; | |
internal Vector2D LastTouch; | |
internal Vector2D TouchLocation = new Vector2D(); | |
internal Vector2D CanvasPosition = new Vector2D(); | |
internal Vector2D PreviousPosition = new Vector2D(); | |
#endregion | |
#region Private properties values | |
private bool _isDrawing; | |
#endregion | |
#region Properties | |
public bool IsDrawing | |
{ | |
get { return _isDrawing; } | |
set | |
{ | |
if (_isDrawing == value) | |
return; | |
_isDrawing = value; | |
LastTouch = null; | |
OnPropertyChanged(); | |
} | |
} | |
#endregion | |
#region Constructors | |
public Ambient2D() | |
{ | |
BackgroundColor = Color.White; | |
VerticalOptions = LayoutOptions.FillAndExpand; | |
HorizontalOptions = LayoutOptions.FillAndExpand; | |
Padding = 0; | |
CreateView(); | |
} | |
#endregion | |
#region Touch Methods | |
private void CanvasViewTouch(object sender, SKTouchEventArgs e) | |
{ | |
if (TouchLocation == null) | |
TouchLocation = new Vector2D(); | |
TouchLocation.X = e.Location.X / CanvasScale - CanvasPosition.X; | |
TouchLocation.Y = e.Location.Y / CanvasScale - CanvasPosition.Y; | |
} | |
internal virtual void DoubleTapGestureTapped(object sender) | |
{ | |
CenterDraw(); | |
StopCanvasActions(); | |
} | |
internal virtual void SingleTapGestureTapped(object sender) | |
{ | |
if (IsZooning) | |
return; | |
if (IsPanning) | |
{ | |
PreviousPosition.X = 0; | |
PreviousPosition.Y = 0; | |
IsPanning = false; | |
} | |
if (!IsDrawing || TouchLocation == null) | |
{ | |
LastTouch = null; | |
CanvasView?.InvalidateSurface(); | |
return; | |
} | |
} | |
internal virtual void PanGesturePanUpdated(object sender, PanUpdatedEventArgs e) | |
{ | |
if (e.StatusType == GestureStatus.Started) | |
{ | |
PreviousPosition.X = e.TotalX.ToSingle(); | |
PreviousPosition.Y = e.TotalY.ToSingle(); | |
IsPanning = true; | |
} | |
else if (e.StatusType == GestureStatus.Running) | |
{ | |
if (!IsPanning) | |
{ | |
PreviousPosition.X = e.TotalX.ToSingle(); | |
PreviousPosition.Y = e.TotalY.ToSingle(); | |
StopCanvasActions(); | |
IsPanning = true; | |
return; | |
} | |
CanvasPosition.X += (((e.TotalX - PreviousPosition.X) / CanvasScale) | |
* TouchSensibility).ToSingle(); | |
CanvasPosition.Y += (((e.TotalY - PreviousPosition.Y)) | |
* TouchSensibility / CanvasScale).ToSingle(); | |
PreviousPosition.X = e.TotalX.ToSingle(); | |
PreviousPosition.Y = e.TotalY.ToSingle(); | |
CanvasView.InvalidateSurface(); | |
} | |
else | |
{ | |
PreviousPosition.X = 0; | |
PreviousPosition.Y = 0; | |
IsPanning = false; | |
} | |
} | |
internal virtual void PinchGesturePinchUpdated(object sender, PinchGestureUpdatedEventArgs e) | |
{ | |
if (e.Status == GestureStatus.Started) | |
{ | |
IsZooning = true; | |
StartCanvasScale = CanvasScale; | |
var touchX = (CanvasView.CanvasSize.Width * e.ScaleOrigin.X).ToSingle(); | |
var touchY = (CanvasView.CanvasSize.Height * e.ScaleOrigin.Y).ToSingle(); | |
} | |
else if (e.Status == GestureStatus.Running) | |
{ | |
var factor = (e.Scale - 1); | |
var delta = factor < 0 ? -1 : 1; | |
factor = Ext.Clamp((factor * delta), 0, MaximumZoomStep) * delta; | |
CanvasScale += ((factor * StartCanvasScale)).ToSingle(); | |
CanvasScale = CanvasScale.Clamp(0.01f, 2); | |
CanvasView.InvalidateSurface(); | |
StartCanvasScale = CanvasScale; | |
} | |
else | |
{ | |
IsZooning = false; | |
} | |
} | |
#endregion | |
#region Private Methods | |
private void CreateView() | |
{ | |
Children.Clear(); | |
CanvasView = new SKCanvasView | |
{ | |
VerticalOptions = LayoutOptions.FillAndExpand, | |
HorizontalOptions = LayoutOptions.FillAndExpand, | |
EnableTouchEvents = true | |
}; | |
CanvasView.PaintSurface += OnCanvasViewPaintSurface; | |
CanvasView.Touch += CanvasViewTouch; | |
CanvasView.EnableTouchEvents = true; | |
Children.Add(CanvasView); | |
var panGesture = new PanGestureRecognizer(); | |
var pinchGesture = new PinchGestureRecognizer(); | |
var singleTapGesture = new TapGestureRecognizer | |
{ | |
Command = new Command((sender) => SingleTapGestureTapped(sender)), | |
NumberOfTapsRequired = 1 | |
}; | |
var doubleTapGesture = new TapGestureRecognizer | |
{ | |
Command = new Command((sender) => DoubleTapGestureTapped(sender)), | |
NumberOfTapsRequired = 2 | |
}; | |
pinchGesture.PinchUpdated += PinchGesturePinchUpdated; | |
panGesture.PanUpdated += PanGesturePanUpdated; | |
GestureRecognizers.Add(pinchGesture); | |
GestureRecognizers.Add(panGesture); | |
GestureRecognizers.Add(doubleTapGesture); | |
GestureRecognizers.Add(singleTapGesture); | |
ResetZoom(); | |
} | |
internal void StopCanvasActions() | |
{ | |
IsZooning = false; | |
IsPanning = false; | |
} | |
#endregion | |
#region Public Methods | |
public void ResetZoom() | |
{ | |
CanvasScale = 0.1f; | |
StartCanvasScale = CanvasScale; | |
CanvasView?.InvalidateSurface(); | |
} | |
public void ResetLocation() | |
{ | |
CanvasPosition.X = 0; | |
CanvasPosition.Y = 0; | |
CanvasView?.InvalidateSurface(); | |
} | |
#endregion | |
#region Virtual Methods | |
internal virtual bool HasAntialias() => | |
CanvasScale > 0.7f; | |
internal virtual void CenterDraw() | |
{ | |
ResetLocation(); | |
} | |
internal virtual void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args) | |
{ | |
var info = args.Info; | |
var surface = args.Surface; | |
var canvas = surface.Canvas; | |
canvas.Scale(CanvasScale, CanvasScale); | |
canvas.Translate(CanvasPosition.X, CanvasPosition.Y); | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment