summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Controls/ControlGalleryPages/PanGestureGalleryPage.cs
blob: f355dfc19cde81145b4b95d235b79682dd622b41 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
´╗┐using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms.Internals;

namespace Xamarin.Forms.Controls
{
	public class PanGestureGalleryPage : ContentPage
	{
		public class PanCompleteArgs : EventArgs
		{
			public PanCompleteArgs(string message) { Message = message; }
			public string Message
			{
				get; private set;
			}
		}

		public class PanContainer : ContentView
		{
			double _x, _y;
			double _currentScale = 1;

			public EventHandler<PanCompleteArgs> PanCompleted;

			public PanContainer()
			{
				GestureRecognizers.Add(GetPinch());
				GestureRecognizers.Add(GetPan());
			}

			PanGestureRecognizer GetPan()
			{
				var pan = new PanGestureRecognizer();
				pan.PanUpdated += (s, e) =>
				{
					switch (e.StatusType)
					{
						case GestureStatus.Running:
							Content.TranslationX = e.TotalX;
							Content.TranslationY = e.TotalY;
							break;

						case GestureStatus.Completed:
							_x = Content.TranslationX;
							_y = Content.TranslationY;

							PanCompleted?.Invoke(s, new PanCompleteArgs($"x: {_x}, y: {_y}"));
							break;
					}
				};
				return pan;
			}

			PinchGestureRecognizer GetPinch()
			{
				var pinch = new PinchGestureRecognizer();

				double xOffset = 0;
				double yOffset = 0;
				double startScale = 1;

				pinch.PinchUpdated += (sender, e) =>
				{

					if (e.Status == GestureStatus.Started)
					{
						startScale = Content.Scale;
						Content.AnchorX = Content.AnchorY = 0;
					}

					if (e.Status == GestureStatus.Running)
					{
						_currentScale += (e.Scale - 1) * startScale;
						_currentScale = Math.Max(1, _currentScale);

						var renderedX = Content.X + xOffset;
						var deltaX = renderedX / Width;
						var deltaWidth = Width / (Content.Width * startScale);
						var originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;

						var renderedY = Content.Y + yOffset;
						var deltaY = renderedY / Height;
						var deltaHeight = Height / (Content.Height * startScale);
						var originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;

						double targetX = xOffset - (originX * Content.Width) * (_currentScale - startScale);
						double targetY = yOffset - (originY * Content.Height) * (_currentScale - startScale);

						Content.TranslationX = targetX.Clamp(-Content.Width * (_currentScale - 1), 0);
						Content.TranslationY = targetY.Clamp(-Content.Height * (_currentScale - 1), 0);

						Content.Scale = _currentScale;
					}

					if (e.Status == GestureStatus.Completed)
					{
						xOffset = Content.TranslationX;
						yOffset = Content.TranslationY;
					}
				};
				return pinch;
			}
		}

		public PanGestureGalleryPage()
		{
			var box = new Image
			{
				BackgroundColor = Color.Gray,
				WidthRequest = 2000,
				HeightRequest = 2000,
				VerticalOptions = LayoutOptions.Center,
				HorizontalOptions = LayoutOptions.Center
			};

			var label = new Label { Text = "Use two fingers to pinch. Use one finger to pan." };

			var panme = new PanContainer { Content = box };
			panme.PanCompleted += (s, e) =>
			{
				label.Text = e.Message;
			};

			Content = new StackLayout { Children = { label, panme }, Padding = new Thickness(20) };
		}
	}
}