AnimationPath.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Windows;
  4. using System.Windows.Media;
  5. using System.Windows.Media.Animation;
  6. using System.Windows.Shapes;
  7. using HandyControl.Data;
  8. using HandyControl.Expression.Drawing;
  9. using HandyControl.Tools.Extension;
  10. namespace HandyControl.Controls;
  11. public class AnimationPath : Shape
  12. {
  13. private Storyboard _storyboard;
  14. private double _pathLength;
  15. public static readonly DependencyProperty DataProperty = DependencyProperty.Register(nameof(Data),
  16. typeof(Geometry), typeof(AnimationPath), new FrameworkPropertyMetadata(null,
  17. OnPropertiesChanged));
  18. private static void OnPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  19. {
  20. if (d is AnimationPath path)
  21. {
  22. path.UpdatePath();
  23. }
  24. }
  25. public Geometry Data
  26. {
  27. get => (Geometry) GetValue(DataProperty);
  28. set => SetValue(DataProperty, value);
  29. }
  30. protected override Geometry DefiningGeometry => Data ?? Geometry.Empty;
  31. public static readonly DependencyProperty PathLengthProperty = DependencyProperty.Register(
  32. nameof(PathLength), typeof(double), typeof(AnimationPath), new FrameworkPropertyMetadata(ValueBoxes.Double0Box, OnPropertiesChanged));
  33. public double PathLength
  34. {
  35. get => (double) GetValue(PathLengthProperty);
  36. set => SetValue(PathLengthProperty, value);
  37. }
  38. public static readonly DependencyProperty DurationProperty = DependencyProperty.Register(
  39. nameof(Duration), typeof(Duration), typeof(AnimationPath), new FrameworkPropertyMetadata(new Duration(TimeSpan.FromSeconds(2)),
  40. OnPropertiesChanged));
  41. public Duration Duration
  42. {
  43. get => (Duration) GetValue(DurationProperty);
  44. set => SetValue(DurationProperty, value);
  45. }
  46. public static readonly DependencyProperty IsPlayingProperty = DependencyProperty.Register(
  47. nameof(IsPlaying), typeof(bool), typeof(AnimationPath), new FrameworkPropertyMetadata(ValueBoxes.TrueBox, (o, args) =>
  48. {
  49. var ctl = (AnimationPath) o;
  50. var v = (bool) args.NewValue;
  51. if (v)
  52. {
  53. ctl.UpdatePath();
  54. }
  55. else
  56. {
  57. ctl._storyboard?.Pause();
  58. }
  59. }));
  60. public bool IsPlaying
  61. {
  62. get => (bool) GetValue(IsPlayingProperty);
  63. set => SetValue(IsPlayingProperty, ValueBoxes.BooleanBox(value));
  64. }
  65. public static readonly DependencyProperty RepeatBehaviorProperty =
  66. Timeline.RepeatBehaviorProperty.AddOwner(typeof(AnimationPath),
  67. new PropertyMetadata(RepeatBehavior.Forever));
  68. public RepeatBehavior RepeatBehavior
  69. {
  70. get => (RepeatBehavior) GetValue(RepeatBehaviorProperty);
  71. set => SetValue(RepeatBehaviorProperty, value);
  72. }
  73. public static readonly DependencyProperty FillBehaviorProperty =
  74. Timeline.FillBehaviorProperty.AddOwner(typeof(AnimationPath), new PropertyMetadata(FillBehavior.Stop));
  75. public FillBehavior FillBehavior
  76. {
  77. get { return (FillBehavior) GetValue(FillBehaviorProperty); }
  78. set { SetValue(FillBehaviorProperty, value); }
  79. }
  80. static AnimationPath()
  81. {
  82. StretchProperty.AddOwner(typeof(AnimationPath), new FrameworkPropertyMetadata(Stretch.Uniform,
  83. OnPropertiesChanged));
  84. StrokeThicknessProperty.AddOwner(typeof(AnimationPath), new FrameworkPropertyMetadata(ValueBoxes.Double1Box,
  85. OnPropertiesChanged));
  86. }
  87. public AnimationPath() => Loaded += (s, e) => UpdatePath();
  88. public static readonly RoutedEvent CompletedEvent =
  89. EventManager.RegisterRoutedEvent("Completed", RoutingStrategy.Bubble,
  90. typeof(EventHandler), typeof(AnimationPath));
  91. public event EventHandler Completed
  92. {
  93. add => AddHandler(CompletedEvent, value);
  94. remove => RemoveHandler(CompletedEvent, value);
  95. }
  96. private void UpdatePath()
  97. {
  98. if (!Duration.HasTimeSpan || !IsPlaying) return;
  99. _pathLength = PathLength > 0 ? PathLength : Data.GetTotalLength(new Size(ActualWidth, ActualHeight), StrokeThickness);
  100. if (MathHelper.IsVerySmall(_pathLength)) return;
  101. StrokeDashOffset = _pathLength;
  102. StrokeDashArray = new DoubleCollection(new List<double>
  103. {
  104. _pathLength,
  105. _pathLength
  106. });
  107. if (_storyboard != null)
  108. {
  109. _storyboard.Stop();
  110. _storyboard.Completed -= Storyboard_Completed;
  111. }
  112. _storyboard = new Storyboard
  113. {
  114. RepeatBehavior = RepeatBehavior,
  115. FillBehavior = FillBehavior
  116. };
  117. _storyboard.Completed += Storyboard_Completed;
  118. var frames = new DoubleAnimationUsingKeyFrames();
  119. var frameIn = new LinearDoubleKeyFrame
  120. {
  121. Value = _pathLength,
  122. KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
  123. };
  124. frames.KeyFrames.Add(frameIn);
  125. var frameOut = new LinearDoubleKeyFrame
  126. {
  127. Value = FillBehavior == FillBehavior.Stop ? -_pathLength : 0,
  128. KeyTime = KeyTime.FromTimeSpan(Duration.TimeSpan)
  129. };
  130. frames.KeyFrames.Add(frameOut);
  131. Storyboard.SetTarget(frames, this);
  132. Storyboard.SetTargetProperty(frames, new PropertyPath(StrokeDashOffsetProperty));
  133. _storyboard.Children.Add(frames);
  134. _storyboard.Begin();
  135. }
  136. private void Storyboard_Completed(object sender, EventArgs e) => RaiseEvent(new RoutedEventArgs(CompletedEvent));
  137. }