From 3aa4105e5339766a2ee8ac67a10f775457925ce0 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Sun, 11 Aug 2019 20:28:35 +0200 Subject: [PATCH] Improve the fling logic --- .../SkiaSharpDemo/GestureSurfacePage.xaml.cs | 19 +++++++++++--- .../SKGestureSurfaceView.FlingTracker.cs | 12 +++++---- .../SKGestureSurfaceView.FlingTrackerEvent.cs | 6 ++--- .../source/SKGestureSurfaceView.cs | 25 ++++++++++--------- 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/SkiaSharp.Extended.Controls/samples/SkiaSharpDemo/GestureSurfacePage.xaml.cs b/SkiaSharp.Extended.Controls/samples/SkiaSharpDemo/GestureSurfacePage.xaml.cs index e0e4363e..f32f3368 100644 --- a/SkiaSharp.Extended.Controls/samples/SkiaSharpDemo/GestureSurfacePage.xaml.cs +++ b/SkiaSharp.Extended.Controls/samples/SkiaSharpDemo/GestureSurfacePage.xaml.cs @@ -31,17 +31,28 @@ public GestureSurfacePage() BindingContext = this; - //gestureSurface.FlingDetected += (sender, e) => - //{ - // gestureSurface.InvalidateSurface(); - //}; + gestureSurface.FlingDetected += (sender, e) => + { + var easing = Easing.SinOut; + + var ratio = e.VelocityX / e.VelocityY; + + gestureSurface.AbortAnimation("Fling"); + var animation = new Animation(v => Transform(new SKPoint((float)(v * ratio), (float)v), SKPoint.Empty, 1, 0), e.VelocityY * 0.01f, 0, easing); + animation.Commit(gestureSurface, "Fling", 16, 1000); + }; + gestureSurface.TransformDetected += (sender, e) => { + gestureSurface.AbortAnimation("Fling"); + Transform(e.Center, e.PreviousCenter, e.ScaleDelta, e.RotationDelta); }; gestureSurface.DoubleTapDetected += (sender, e) => { + gestureSurface.AbortAnimation("Fling"); + Transform(e.Location, e.Location, 1.5f, 0); }; } diff --git a/SkiaSharp.Extended.Controls/source/SKGestureSurfaceView.FlingTracker.cs b/SkiaSharp.Extended.Controls/source/SKGestureSurfaceView.FlingTracker.cs index 63fa62db..43be7e57 100644 --- a/SkiaSharp.Extended.Controls/source/SKGestureSurfaceView.FlingTracker.cs +++ b/SkiaSharp.Extended.Controls/source/SKGestureSurfaceView.FlingTracker.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace SkiaSharp.Extended.Controls { @@ -6,7 +7,7 @@ public partial class SKGestureSurfaceView { private class FlingTracker { - private const long maxTicks = 200 * 10000; // Use only events from the last 200 ms + private const long thresholdTicks = 200 * TimeSpan.TicksPerMillisecond; // Use only events from the last 200 ms private const int maxSize = 2; @@ -50,12 +51,13 @@ public SKPoint CalculateVelocity(long id, long now) var nowItem = array[1]; // use last 2 events - if (now - lastItem.Time < maxTicks) + if (now - lastItem.TimeTicks < thresholdTicks) { - velocityX = (nowItem.X - lastItem.X) * 10000000 / (nowItem.Time - lastItem.Time); - velocityY = (nowItem.Y - lastItem.Y) * 10000000 / (nowItem.Time - lastItem.Time); + velocityX = (nowItem.X - lastItem.X) * TimeSpan.TicksPerSecond / (nowItem.TimeTicks - lastItem.TimeTicks); + velocityY = (nowItem.Y - lastItem.Y) * TimeSpan.TicksPerSecond / (nowItem.TimeTicks - lastItem.TimeTicks); } + // return the velocity in pixels per second return new SKPoint(velocityX, velocityY); } } diff --git a/SkiaSharp.Extended.Controls/source/SKGestureSurfaceView.FlingTrackerEvent.cs b/SkiaSharp.Extended.Controls/source/SKGestureSurfaceView.FlingTrackerEvent.cs index 7f68492d..5e01dbd8 100644 --- a/SkiaSharp.Extended.Controls/source/SKGestureSurfaceView.FlingTrackerEvent.cs +++ b/SkiaSharp.Extended.Controls/source/SKGestureSurfaceView.FlingTrackerEvent.cs @@ -4,18 +4,18 @@ public partial class SKGestureSurfaceView { private struct FlingTrackerEvent { - public FlingTrackerEvent(float x, float y, long time) + public FlingTrackerEvent(float x, float y, long timeTicks) { X = x; Y = y; - Time = time; + TimeTicks = timeTicks; } public float X { get; } public float Y { get; } - public long Time { get; } + public long TimeTicks { get; } } } } diff --git a/SkiaSharp.Extended.Controls/source/SKGestureSurfaceView.cs b/SkiaSharp.Extended.Controls/source/SKGestureSurfaceView.cs index a5201362..09f41ec1 100644 --- a/SkiaSharp.Extended.Controls/source/SKGestureSurfaceView.cs +++ b/SkiaSharp.Extended.Controls/source/SKGestureSurfaceView.cs @@ -1,23 +1,24 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using SkiaSharp.Views.Forms; namespace SkiaSharp.Extended.Controls { public partial class SKGestureSurfaceView : SKDynamicSurfaceView { - private const int shortTap = 125; - private const int shortClick = 250; - private const int delayTap = 200; - private const int longTap = 500; - private const int touchSlop = 8; - private const int flingVelocity = 200; + private const long shortTapTicks = 125 * TimeSpan.TicksPerMillisecond; + private const long shortClickTicks = 250 * TimeSpan.TicksPerMillisecond; + private const int delayTapMilliseconds = 200; + private const long longTapTicks = 500 * TimeSpan.TicksPerMillisecond; + private const int touchSlopPixels = 8; + private const int flingVelocityThreshold = 200; // pixels per second private readonly Dictionary touches = new Dictionary(); private readonly FlingTracker flingTracker = new FlingTracker(); private SKPoint initialTouch = SKPoint.Empty; - private System.Threading.Timer multiTapTimer; + private Timer multiTapTimer; private int tapCount = 0; private TouchMode touchMode = TouchMode.None; private PinchValue previousValues; @@ -226,7 +227,7 @@ private bool OnTouchReleased(SKTouchEventArgs e) { // check to see if it was a fling var velocity = flingTracker.CalculateVelocity(e.Id, ticks); - if (Math.Abs(velocity.X) > flingVelocity || Math.Abs(velocity.Y) > flingVelocity) + if (Math.Abs(velocity.X * velocity.Y) > (flingVelocityThreshold * flingVelocityThreshold)) { var args = new SKFlingDetectedEventArgs(velocity.X, velocity.Y); OnFlingDetected(args); @@ -234,8 +235,8 @@ private bool OnTouchReleased(SKTouchEventArgs e) } // when tapping, the finger never goes to exactly the same location - var isAround = SKPoint.Distance(releasedTouch.Location, initialTouch) < touchSlop; - if (isAround && (ticks - releasedTouch.Tick) < (e.DeviceType == SKTouchDeviceType.Mouse ? shortClick : longTap) * 10000) + var isAround = SKPoint.Distance(releasedTouch.Location, initialTouch) < touchSlopPixels; + if (isAround && (ticks - releasedTouch.Tick) < (e.DeviceType == SKTouchDeviceType.Mouse ? shortClickTicks : longTapTicks)) { // add a timer to detect the type of tap (single or multi) void TimerHandler(object state) @@ -260,9 +261,9 @@ void TimerHandler(object state) multiTapTimer?.Dispose(); multiTapTimer = null; }; - multiTapTimer = new System.Threading.Timer(TimerHandler, location, delayTap, -1); + multiTapTimer = new Timer(TimerHandler, location, delayTapMilliseconds, -1); } - else if (isAround && (ticks - releasedTouch.Tick) >= longTap * 10000) + else if (isAround && (ticks - releasedTouch.Tick) >= longTapTicks) { // if the finger was down for a long time, then it is a long tap if (!handled)