GesturePage.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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. if (this.Find<Slider>("AngleSlider") is { } slider &&
  51. this.Find<Panel>("RotationGesture") is { } rotationGesture
  52. )
  53. {
  54. rotationGesture.AddHandler(Gestures.PinchEvent, (s, e) =>
  55. {
  56. slider.Value = e.Angle;
  57. });
  58. }
  59. }
  60. private void SetPinchHandlers(Control? control)
  61. {
  62. if (control == null)
  63. {
  64. return;
  65. }
  66. _currentScale = 1;
  67. Vector3D currentOffset = default;
  68. CompositionVisual? compositionVisual = null;
  69. void InitComposition(Control visual)
  70. {
  71. if (compositionVisual != null)
  72. {
  73. return;
  74. }
  75. compositionVisual = ElementComposition.GetElementVisual(visual);
  76. }
  77. control.LayoutUpdated += (s, e) =>
  78. {
  79. InitComposition(control!);
  80. if (compositionVisual != null)
  81. {
  82. compositionVisual.Scale = new(_currentScale, _currentScale, 1);
  83. if(currentOffset == default)
  84. {
  85. currentOffset = compositionVisual.Offset;
  86. }
  87. }
  88. };
  89. control.AddHandler(Gestures.PinchEvent, (s, e) =>
  90. {
  91. InitComposition(control!);
  92. if(compositionVisual != null)
  93. {
  94. var scale = _currentScale * (float)e.Scale;
  95. if (scale <= 1)
  96. {
  97. scale = 1;
  98. compositionVisual.Offset = default;
  99. }
  100. compositionVisual.Scale = new(scale, scale, 1);
  101. e.Handled = true;
  102. }
  103. });
  104. control.AddHandler(Gestures.PinchEndedEvent, (s, e) =>
  105. {
  106. InitComposition(control!);
  107. if (compositionVisual != null)
  108. {
  109. _currentScale = compositionVisual.Scale.X;
  110. }
  111. });
  112. control.AddHandler(Gestures.ScrollGestureEvent, (s, e) =>
  113. {
  114. InitComposition(control!);
  115. if (compositionVisual != null && _currentScale != 1)
  116. {
  117. currentOffset += new Vector3D(e.Delta.X, e.Delta.Y, 0);
  118. var currentSize = control.Bounds.Size * _currentScale;
  119. currentOffset = new Vector3D(MathUtilities.Clamp(currentOffset.X, 0, currentSize.Width - control.Bounds.Width),
  120. (float)MathUtilities.Clamp(currentOffset.Y, 0, currentSize.Height - control.Bounds.Height),
  121. 0);
  122. compositionVisual.Offset = currentOffset * -1;
  123. e.Handled = true;
  124. }
  125. });
  126. }
  127. private void SetPullHandlers(Control? control, bool inverse)
  128. {
  129. if (control == null)
  130. {
  131. return;
  132. }
  133. var ball = control.FindLogicalDescendantOfType<Border>();
  134. Vector3D defaultOffset = default;
  135. CompositionVisual? ballCompositionVisual = null;
  136. if (ball != null)
  137. {
  138. InitComposition(ball);
  139. }
  140. else
  141. {
  142. return;
  143. }
  144. control.LayoutUpdated += (s, e) =>
  145. {
  146. InitComposition(ball!);
  147. if (ballCompositionVisual != null)
  148. {
  149. defaultOffset = ballCompositionVisual.Offset;
  150. }
  151. };
  152. control.AddHandler(Gestures.PullGestureEvent, (s, e) =>
  153. {
  154. Vector3D center = new((float)control.Bounds.Center.X, (float)control.Bounds.Center.Y, 0);
  155. InitComposition(ball!);
  156. if (ballCompositionVisual != null)
  157. {
  158. ballCompositionVisual.Offset = defaultOffset + new Vector3D(e.Delta.X * 0.4f, e.Delta.Y * 0.4f, 0) * (inverse ? -1 : 1);
  159. e.Handled = true;
  160. }
  161. });
  162. control.AddHandler(Gestures.PullGestureEndedEvent, (s, e) =>
  163. {
  164. InitComposition(ball!);
  165. if (ballCompositionVisual != null)
  166. {
  167. ballCompositionVisual.Offset = defaultOffset;
  168. }
  169. });
  170. void InitComposition(Control control)
  171. {
  172. ballCompositionVisual = ElementComposition.GetElementVisual(ball);
  173. if (ballCompositionVisual != null)
  174. {
  175. var offsetAnimation = ballCompositionVisual.Compositor.CreateVector3KeyFrameAnimation();
  176. offsetAnimation.Target = "Offset";
  177. offsetAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue");
  178. offsetAnimation.Duration = TimeSpan.FromMilliseconds(100);
  179. var implicitAnimations = ballCompositionVisual.Compositor.CreateImplicitAnimationCollection();
  180. implicitAnimations["Offset"] = offsetAnimation;
  181. ballCompositionVisual.ImplicitAnimations = implicitAnimations;
  182. }
  183. }
  184. }
  185. }
  186. }