Files
HandyControl/src/Shared/HandyControl_Shared/Controls/Other/AnimationPath.cs
T

201 lines
6.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using HandyControl.Data;
using HandyControl.Expression.Drawing;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
{
public class AnimationPath : Shape
{
/// <summary>
/// 故事板
/// </summary>
private Storyboard _storyboard;
/// <summary>
/// 路径长度
/// </summary>
private double _pathLength;
/// <summary>
/// 路径
/// </summary>
public static readonly DependencyProperty DataProperty = DependencyProperty.Register(nameof(Data),
typeof(Geometry), typeof(AnimationPath), new FrameworkPropertyMetadata(null,
OnPropertiesChanged));
private static void OnPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is AnimationPath path)
{
path.UpdatePath();
}
}
/// <summary>
/// 路径
/// </summary>
public Geometry Data
{
get => (Geometry) GetValue(DataProperty);
set => SetValue(DataProperty, value);
}
protected override Geometry DefiningGeometry => Data ?? Geometry.Empty;
/// <summary>
/// 路径长度
/// </summary>
public static readonly DependencyProperty PathLengthProperty = DependencyProperty.Register(
"PathLength", typeof(double), typeof(AnimationPath), new FrameworkPropertyMetadata(ValueBoxes.Double0Box, OnPropertiesChanged));
/// <summary>
/// 路径长度
/// </summary>
public double PathLength
{
get => (double) GetValue(PathLengthProperty);
set => SetValue(PathLengthProperty, value);
}
/// <summary>
/// 动画间隔时间
/// </summary>
public static readonly DependencyProperty DurationProperty = DependencyProperty.Register(
"Duration", typeof(Duration), typeof(AnimationPath), new FrameworkPropertyMetadata(new Duration(TimeSpan.FromSeconds(2)),
OnPropertiesChanged));
/// <summary>
/// 动画间隔时间
/// </summary>
public Duration Duration
{
get => (Duration) GetValue(DurationProperty);
set => SetValue(DurationProperty, value);
}
public static readonly DependencyProperty IsPlayingProperty = DependencyProperty.Register(
"IsPlaying", typeof(bool), typeof(AnimationPath), new FrameworkPropertyMetadata(ValueBoxes.TrueBox, (o, args) =>
{
var ctl = (AnimationPath) o;
var v = (bool) args.NewValue;
if (v)
{
ctl.UpdatePath();
}
else
{
ctl._storyboard?.Pause();
}
}));
/// <summary>
/// 是否正在播放动画
/// </summary>
public bool IsPlaying
{
get => (bool) GetValue(IsPlayingProperty);
set => SetValue(IsPlayingProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty RepeatBehaviorProperty = DependencyProperty.Register(
"RepeatBehavior", typeof(RepeatBehavior), typeof(AnimationPath), new PropertyMetadata(RepeatBehavior.Forever));
/// <summary>
/// 动画重复行为
/// </summary>
public RepeatBehavior RepeatBehavior
{
get => (RepeatBehavior) GetValue(RepeatBehaviorProperty);
set => SetValue(RepeatBehaviorProperty, value);
}
static AnimationPath()
{
StretchProperty.AddOwner(typeof(AnimationPath), new FrameworkPropertyMetadata(Stretch.Uniform,
OnPropertiesChanged));
StrokeThicknessProperty.AddOwner(typeof(AnimationPath), new FrameworkPropertyMetadata(ValueBoxes.Double1Box,
OnPropertiesChanged));
}
public AnimationPath() => Loaded += (s, e) => UpdatePath();
/// <summary>
/// 动画完成事件
/// </summary>
public static readonly RoutedEvent CompletedEvent =
EventManager.RegisterRoutedEvent("Completed", RoutingStrategy.Bubble,
typeof(EventHandler), typeof(AnimationPath));
/// <summary>
/// 动画完成事件
/// </summary>
public event EventHandler Completed
{
add => AddHandler(CompletedEvent, value);
remove => RemoveHandler(CompletedEvent, value);
}
/// <summary>
/// 更新路径
/// </summary>
private void UpdatePath()
{
if (!Duration.HasTimeSpan || !IsPlaying) return;
_pathLength = PathLength > 0 ? PathLength : Data.GetTotalLength(new Size(ActualWidth, ActualHeight), StrokeThickness);
if (MathHelper.IsVerySmall(_pathLength)) return;
StrokeDashOffset = _pathLength;
StrokeDashArray = new DoubleCollection(new List<double>
{
_pathLength,
_pathLength
});
//定义动画
if (_storyboard != null)
{
_storyboard.Stop();
_storyboard.Completed -= Storyboard_Completed;
}
_storyboard = new Storyboard
{
RepeatBehavior = RepeatBehavior
};
_storyboard.Completed += Storyboard_Completed;
var frames = new DoubleAnimationUsingKeyFrames();
//开始位置
var frame0 = new LinearDoubleKeyFrame
{
Value = _pathLength,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
//结束位置
var frame1 = new LinearDoubleKeyFrame
{
Value = -_pathLength,
KeyTime = KeyTime.FromTimeSpan(Duration.TimeSpan)
};
frames.KeyFrames.Add(frame0);
frames.KeyFrames.Add(frame1);
Storyboard.SetTarget(frames, this);
Storyboard.SetTargetProperty(frames, new PropertyPath(StrokeDashOffsetProperty));
_storyboard.Children.Add(frames);
_storyboard.Begin();
}
private void Storyboard_Completed(object sender, EventArgs e) => RaiseEvent(new RoutedEventArgs(CompletedEvent));
}
}