PageSlideTransition.cs 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. using Avalonia;
  6. using Avalonia.Animation;
  7. using Avalonia.Animation.Easings;
  8. using Avalonia.Media;
  9. using Avalonia.Styling;
  10. using Avalonia.VisualTree;
  11. namespace ControlCatalog.Pages
  12. {
  13. /// <summary>
  14. /// Example custom IPageTransition: a directional page slide.
  15. /// Both pages slide together in the specified axis direction.
  16. /// Demonstrates how to implement a custom horizontal or vertical slide from scratch.
  17. /// </summary>
  18. public class PageSlideTransition : IPageTransition
  19. {
  20. public enum SlideAxis { Horizontal, Vertical }
  21. public PageSlideTransition() { }
  22. public PageSlideTransition(TimeSpan duration, SlideAxis axis = SlideAxis.Horizontal)
  23. {
  24. Duration = duration;
  25. Axis = axis;
  26. }
  27. public TimeSpan Duration { get; set; } = TimeSpan.FromMilliseconds(300);
  28. public SlideAxis Axis { get; set; } = SlideAxis.Horizontal;
  29. public Easing SlideEasing { get; set; } = new LinearEasing();
  30. public Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken) =>
  31. Axis == SlideAxis.Horizontal
  32. ? StartAxis(from, to, forward, cancellationToken, TranslateTransform.XProperty, () => GetVisualParent(from, to).Bounds.Width)
  33. : StartAxis(from, to, forward, cancellationToken, TranslateTransform.YProperty, () => GetVisualParent(from, to).Bounds.Height);
  34. private async Task StartAxis(
  35. Visual? from, Visual? to, bool forward, CancellationToken cancellationToken,
  36. Avalonia.AvaloniaProperty<double> prop, Func<double> getDistance)
  37. {
  38. if (cancellationToken.IsCancellationRequested)
  39. return;
  40. var tasks = new List<Task>();
  41. var distance = getDistance() is > 0 and var d ? d : 500d;
  42. if (from != null)
  43. {
  44. var anim = new Avalonia.Animation.Animation
  45. {
  46. FillMode = FillMode.Forward,
  47. Easing = SlideEasing,
  48. Duration = Duration,
  49. Children =
  50. {
  51. new KeyFrame { Cue = new Cue(0d), Setters = { new Setter(prop, 0d) } },
  52. new KeyFrame { Cue = new Cue(1d), Setters = { new Setter(prop, forward ? -distance : distance) } }
  53. }
  54. };
  55. tasks.Add(anim.RunAsync(from, cancellationToken));
  56. }
  57. if (to != null)
  58. {
  59. to.IsVisible = true;
  60. var anim = new Avalonia.Animation.Animation
  61. {
  62. FillMode = FillMode.Forward,
  63. Easing = SlideEasing,
  64. Duration = Duration,
  65. Children =
  66. {
  67. new KeyFrame { Cue = new Cue(0d), Setters = { new Setter(prop, forward ? distance : -distance) } },
  68. new KeyFrame { Cue = new Cue(1d), Setters = { new Setter(prop, 0d) } }
  69. }
  70. };
  71. tasks.Add(anim.RunAsync(to, cancellationToken));
  72. }
  73. await Task.WhenAll(tasks);
  74. if (from != null && !cancellationToken.IsCancellationRequested)
  75. from.IsVisible = false;
  76. }
  77. private static Visual GetVisualParent(Visual? from, Visual? to)
  78. {
  79. var p1 = (from ?? to)!.GetVisualParent();
  80. if (from != null && to != null &&
  81. !ReferenceEquals(from.GetVisualParent(), to.GetVisualParent()))
  82. throw new ArgumentException("Transition elements have different parents.");
  83. return p1 ?? throw new ArgumentException("Transition elements have no parent.");
  84. }
  85. }
  86. }