ParallaxSlideTransition.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  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 parallax slide.
  15. /// The incoming page slides full-width from the right while the outgoing page shifts ~30%
  16. /// to the left with a subtle opacity fade, producing a depth-layered push effect.
  17. /// </summary>
  18. public class ParallaxSlideTransition : IPageTransition
  19. {
  20. public ParallaxSlideTransition() { }
  21. public ParallaxSlideTransition(TimeSpan duration)
  22. {
  23. Duration = duration;
  24. }
  25. public TimeSpan Duration { get; set; } = TimeSpan.FromMilliseconds(350);
  26. public Easing SlideEasing { get; set; } = new CubicEaseOut();
  27. public async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken)
  28. {
  29. if (cancellationToken.IsCancellationRequested)
  30. return;
  31. var tasks = new List<Task>();
  32. var parent = GetVisualParent(from, to);
  33. var distance = parent.Bounds.Width > 0 ? parent.Bounds.Width : 500d;
  34. if (from != null)
  35. {
  36. var anim = new Avalonia.Animation.Animation
  37. {
  38. FillMode = FillMode.Forward,
  39. Easing = SlideEasing,
  40. Duration = Duration,
  41. Children =
  42. {
  43. new KeyFrame
  44. {
  45. Cue = new Cue(0d),
  46. Setters =
  47. {
  48. new Setter(TranslateTransform.XProperty, 0d),
  49. new Setter(Visual.OpacityProperty, 1d)
  50. }
  51. },
  52. new KeyFrame
  53. {
  54. Cue = new Cue(1d),
  55. Setters =
  56. {
  57. new Setter(TranslateTransform.XProperty, forward ? -distance * 0.3 : distance),
  58. new Setter(Visual.OpacityProperty, forward ? 0.7 : 1d)
  59. }
  60. }
  61. }
  62. };
  63. tasks.Add(anim.RunAsync(from, cancellationToken));
  64. }
  65. if (to != null)
  66. {
  67. to.IsVisible = true;
  68. var anim = new Avalonia.Animation.Animation
  69. {
  70. FillMode = FillMode.Forward,
  71. Easing = SlideEasing,
  72. Duration = Duration,
  73. Children =
  74. {
  75. new KeyFrame
  76. {
  77. Cue = new Cue(0d),
  78. Setters =
  79. {
  80. new Setter(TranslateTransform.XProperty, forward ? distance : -distance * 0.3),
  81. new Setter(Visual.OpacityProperty, forward ? 1d : 0.7)
  82. }
  83. },
  84. new KeyFrame
  85. {
  86. Cue = new Cue(1d),
  87. Setters =
  88. {
  89. new Setter(TranslateTransform.XProperty, 0d),
  90. new Setter(Visual.OpacityProperty, 1d)
  91. }
  92. }
  93. }
  94. };
  95. tasks.Add(anim.RunAsync(to, cancellationToken));
  96. }
  97. await Task.WhenAll(tasks);
  98. if (from != null && !cancellationToken.IsCancellationRequested)
  99. from.IsVisible = false;
  100. }
  101. private static Visual GetVisualParent(Visual? from, Visual? to)
  102. {
  103. var p1 = (from ?? to)!.GetVisualParent();
  104. if (from != null && to != null &&
  105. !ReferenceEquals(from.GetVisualParent(), to.GetVisualParent()))
  106. throw new ArgumentException("Transition elements have different parents.");
  107. return p1 ?? throw new ArgumentException("Transition elements have no parent.");
  108. }
  109. }
  110. }