GesturePage.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. using System;
  2. using System.Numerics;
  3. using Avalonia;
  4. using Avalonia.Controls;
  5. using Avalonia.Input;
  6. using Avalonia.LogicalTree;
  7. using Avalonia.Markup.Xaml;
  8. using Avalonia.Rendering.Composition;
  9. using Avalonia.Utilities;
  10. namespace ControlCatalog.Pages
  11. {
  12. public class GesturePage : UserControl
  13. {
  14. private bool _isInit;
  15. private double _currentScale;
  16. public GesturePage()
  17. {
  18. this.InitializeComponent();
  19. }
  20. private void InitializeComponent()
  21. {
  22. AvaloniaXamlLoader.Load(this);
  23. }
  24. protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
  25. {
  26. base.OnAttachedToVisualTree(e);
  27. if(_isInit)
  28. {
  29. return;
  30. }
  31. _isInit = true;
  32. SetPullHandlers(this.Find<Border>("TopPullZone"), false);
  33. SetPullHandlers(this.Find<Border>("BottomPullZone"), true);
  34. SetPullHandlers(this.Find<Border>("RightPullZone"), true);
  35. SetPullHandlers(this.Find<Border>("LeftPullZone"), false);
  36. var image = this.Get<Image>("PinchImage");
  37. SetPinchHandlers(image);
  38. var reset = this.Get<Button>("ResetButton");
  39. reset.Click += (_, _) =>
  40. {
  41. var compositionVisual = ElementComposition.GetElementVisual(image);
  42. if(compositionVisual!= null)
  43. {
  44. _currentScale = 1;
  45. compositionVisual.Scale = new (1,1,1);
  46. compositionVisual.Offset = default;
  47. image.InvalidateMeasure();
  48. }
  49. };
  50. }
  51. private void SetPinchHandlers(Control? control)
  52. {
  53. if (control == null)
  54. {
  55. return;
  56. }
  57. _currentScale = 1;
  58. Vector3D currentOffset = default;
  59. CompositionVisual? compositionVisual = null;
  60. void InitComposition(Control visual)
  61. {
  62. if (compositionVisual != null)
  63. {
  64. return;
  65. }
  66. compositionVisual = ElementComposition.GetElementVisual(visual);
  67. }
  68. control.LayoutUpdated += (s, e) =>
  69. {
  70. InitComposition(control!);
  71. if (compositionVisual != null)
  72. {
  73. compositionVisual.Scale = new(_currentScale, _currentScale, 1);
  74. if(currentOffset == default)
  75. {
  76. currentOffset = compositionVisual.Offset;
  77. }
  78. }
  79. };
  80. control.AddHandler(Gestures.PinchEvent, (s, e) =>
  81. {
  82. InitComposition(control!);
  83. if(compositionVisual != null)
  84. {
  85. var scale = _currentScale * (float)e.Scale;
  86. if (scale <= 1)
  87. {
  88. scale = 1;
  89. compositionVisual.Offset = default;
  90. }
  91. compositionVisual.Scale = new(scale, scale, 1);
  92. e.Handled = true;
  93. }
  94. });
  95. control.AddHandler(Gestures.PinchEndedEvent, (s, e) =>
  96. {
  97. InitComposition(control!);
  98. if (compositionVisual != null)
  99. {
  100. _currentScale = compositionVisual.Scale.X;
  101. }
  102. });
  103. control.AddHandler(Gestures.ScrollGestureEvent, (s, e) =>
  104. {
  105. InitComposition(control!);
  106. if (compositionVisual != null && _currentScale != 1)
  107. {
  108. currentOffset += new Vector3D(e.Delta.X, e.Delta.Y, 0);
  109. var currentSize = control.Bounds.Size * _currentScale;
  110. currentOffset = new Vector3D(MathUtilities.Clamp(currentOffset.X, 0, currentSize.Width - control.Bounds.Width),
  111. (float)MathUtilities.Clamp(currentOffset.Y, 0, currentSize.Height - control.Bounds.Height),
  112. 0);
  113. compositionVisual.Offset = currentOffset * -1;
  114. e.Handled = true;
  115. }
  116. });
  117. }
  118. private void SetPullHandlers(Control? control, bool inverse)
  119. {
  120. if (control == null)
  121. {
  122. return;
  123. }
  124. var ball = control.FindLogicalDescendantOfType<Border>();
  125. Vector3D defaultOffset = default;
  126. CompositionVisual? ballCompositionVisual = null;
  127. if (ball != null)
  128. {
  129. InitComposition(ball);
  130. }
  131. else
  132. {
  133. return;
  134. }
  135. control.LayoutUpdated += (s, e) =>
  136. {
  137. InitComposition(ball!);
  138. if (ballCompositionVisual != null)
  139. {
  140. defaultOffset = ballCompositionVisual.Offset;
  141. }
  142. };
  143. control.AddHandler(Gestures.PullGestureEvent, (s, e) =>
  144. {
  145. Vector3D center = new((float)control.Bounds.Center.X, (float)control.Bounds.Center.Y, 0);
  146. InitComposition(ball!);
  147. if (ballCompositionVisual != null)
  148. {
  149. ballCompositionVisual.Offset = defaultOffset + new Vector3D(e.Delta.X * 0.4f, e.Delta.Y * 0.4f, 0) * (inverse ? -1 : 1);
  150. e.Handled = true;
  151. }
  152. });
  153. control.AddHandler(Gestures.PullGestureEndedEvent, (s, e) =>
  154. {
  155. InitComposition(ball!);
  156. if (ballCompositionVisual != null)
  157. {
  158. ballCompositionVisual.Offset = defaultOffset;
  159. }
  160. });
  161. void InitComposition(Control control)
  162. {
  163. ballCompositionVisual = ElementComposition.GetElementVisual(ball);
  164. if (ballCompositionVisual != null)
  165. {
  166. var offsetAnimation = ballCompositionVisual.Compositor.CreateVector3KeyFrameAnimation();
  167. offsetAnimation.Target = "Offset";
  168. offsetAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue");
  169. offsetAnimation.Duration = TimeSpan.FromMilliseconds(100);
  170. var implicitAnimations = ballCompositionVisual.Compositor.CreateImplicitAnimationCollection();
  171. implicitAnimations["Offset"] = offsetAnimation;
  172. ballCompositionVisual.ImplicitAnimations = implicitAnimations;
  173. }
  174. }
  175. }
  176. }
  177. }