From 814bfc2d6439761bdd6a023d433859e365947cf4 Mon Sep 17 00:00:00 2001
From: NaBian <836904362@qq.com>
Date: Thu, 26 Sep 2019 00:32:00 +0800
Subject: [PATCH] add basic codes
---
.../HandyControl_Shared.projitems | 2 +
.../Media/Animation/GeometryAnimation.cs | 222 ++++++++++++++++++
.../Media/Animation/GeometryAnimationBase.cs | 56 +++++
.../Properties/AssemblyInfo.cs | 1 +
.../Tools/Helper/AnimationHelper.cs | 2 +-
5 files changed, 282 insertions(+), 1 deletion(-)
create mode 100644 src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimation.cs
create mode 100644 src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationBase.cs
diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
index ae517b09..22182e67 100644
--- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
+++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
@@ -41,6 +41,8 @@
+
+
diff --git a/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimation.cs b/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimation.cs
new file mode 100644
index 00000000..85c818e4
--- /dev/null
+++ b/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimation.cs
@@ -0,0 +1,222 @@
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using HandyControl.Tools.Extension;
+// ReSharper disable PossibleInvalidOperationException
+
+namespace HandyControl.Media.Animation
+{
+ public class GeometryAnimation : GeometryAnimationBase
+ {
+ private const string Rgx = @"[+-]?\d*\.?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?";
+
+ private string[] _strings;
+
+ private double[] _numbersFrom;
+
+ private double[] _numbersTo;
+
+ private double[] _numbersAccumulator;
+
+ private double[][] _keyValues;
+
+ private bool _isAnimationFunctionValid;
+
+ public GeometryAnimation()
+ {
+
+ }
+
+ public GeometryAnimation(string fromValue, string toValue) : this()
+ {
+ From = Geometry.Parse(fromValue);
+ To = Geometry.Parse(toValue);
+ }
+
+ private void UpdateValue()
+ {
+ if (_numbersFrom == null || _numbersTo == null || _numbersFrom.Length != _numbersTo.Length) return;
+
+ _numbersAccumulator = new double[_numbersFrom.Length];
+ for (var i = 0; i < _numbersFrom.Length; i++)
+ {
+ _numbersAccumulator[i] = _numbersTo[i] - _numbersFrom[i];
+ }
+ }
+
+ private static void DecomposeValue(string geometryStr, out double[] arr)
+ {
+ var collection = Regex.Matches(geometryStr, Rgx);
+ arr = new double[collection.Count];
+ for (var i = 0; i < collection.Count; i++)
+ {
+ arr[i] = collection[i].Value.Value();
+ }
+ }
+
+ public GeometryAnimation(Geometry fromValue, Geometry toValue) : this()
+ {
+ From = fromValue;
+ To = toValue;
+ }
+
+ public GeometryAnimation(Geometry fromValue, Geometry toValue, Duration duration) : this()
+ {
+ From = fromValue;
+ To = toValue;
+ Duration = duration;
+ }
+
+ public GeometryAnimation(Geometry fromValue, Geometry toValue, Duration duration, FillBehavior fillBehavior) : this()
+ {
+ From = fromValue;
+ To = toValue;
+ Duration = duration;
+ FillBehavior = fillBehavior;
+ }
+
+ public new GeometryAnimation Clone() => (GeometryAnimation)base.Clone();
+
+ protected override Freezable CreateInstanceCore() => new GeometryAnimation();
+
+ private Geometry ConvertToGeometry(double[] arr)
+ {
+ var builder = new StringBuilder(_strings[0]);
+ for (var i = 0; i < _numbersAccumulator.Length; i++)
+ {
+ var s = _strings[i + 1];
+ var n = arr[i];
+ if (!double.IsNaN(n))
+ {
+ builder.Append(n).Append(s);
+ }
+ }
+
+ return Geometry.Parse(builder.ToString());
+ }
+
+ protected override Geometry GetCurrentValueCore(Geometry defaultOriginValue, Geometry defaultDestinationValue, AnimationClock animationClock)
+ {
+ if (!_isAnimationFunctionValid)
+ {
+ ValidateAnimationFunction();
+ }
+
+ var progress = animationClock.CurrentProgress.Value;
+
+ var easingFunction = EasingFunction;
+ if (easingFunction != null)
+ {
+ progress = easingFunction.Ease(progress);
+ }
+
+ var accumulated = new double[_numbersAccumulator.Length];
+ var foundation = new double[_numbersAccumulator.Length];
+
+ var from = _keyValues[0];
+
+ if (IsAdditive)
+ {
+ foundation = new double[_numbersAccumulator.Length];
+ }
+
+
+ if (IsCumulative)
+ {
+ var currentRepeat = (double)(animationClock.CurrentIteration - 1);
+
+ if (currentRepeat > 0.0)
+ {
+ accumulated = new double[_numbersAccumulator.Length];
+ for (var i = 0; i < _numbersAccumulator.Length; i++)
+ {
+ accumulated[i] = _numbersAccumulator[i] * currentRepeat;
+ }
+ }
+ }
+
+ var numbers = new double[_numbersAccumulator.Length];
+
+ for (var i = 0; i < _numbersAccumulator.Length; i++)
+ {
+ numbers[i] = foundation[i] + accumulated[i] + from[i] + _numbersAccumulator[i] * progress;
+ }
+
+ return ConvertToGeometry(numbers);
+ }
+
+ private void ValidateAnimationFunction()
+ {
+ _keyValues = null;
+
+ _keyValues = new double[2][];
+ _keyValues[0] = _numbersFrom;
+ _keyValues[1] = _numbersTo;
+
+ _isAnimationFunctionValid = true;
+ }
+
+ public static readonly DependencyProperty FromProperty = DependencyProperty.Register(
+ "From", typeof(Geometry), typeof(GeometryAnimation), new PropertyMetadata(default(Geometry), OnFromChanged));
+
+ private static void OnFromChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var obj = (GeometryAnimation) d;
+ if (e.NewValue is Geometry geometry)
+ {
+ DecomposeValue(geometry.ToString(), out obj._numbersFrom);
+ obj.UpdateValue();
+ }
+ }
+
+ public Geometry From
+ {
+ get => (Geometry)GetValue(FromProperty);
+ set => SetValue(FromProperty, value);
+ }
+
+ public static readonly DependencyProperty ToProperty = DependencyProperty.Register(
+ "To", typeof(Geometry), typeof(GeometryAnimation), new PropertyMetadata(default(Geometry), OnToChanged));
+
+ private static void OnToChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var obj = (GeometryAnimation)d;
+ if (e.NewValue is Geometry geometry)
+ {
+ var geometryStr = geometry.ToString();
+ DecomposeValue(geometryStr, out obj._numbersTo);
+ obj._strings = Regex.Split(geometryStr, Rgx);
+ obj.UpdateValue();
+ }
+ }
+
+ public Geometry To
+ {
+ get => (Geometry)GetValue(ToProperty);
+ set => SetValue(ToProperty, value);
+ }
+
+ public static readonly DependencyProperty EasingFunctionProperty = DependencyProperty.Register(
+ "EasingFunction", typeof(IEasingFunction), typeof(GeometryAnimation), new PropertyMetadata(default(IEasingFunction)));
+
+ public IEasingFunction EasingFunction
+ {
+ get => (IEasingFunction)GetValue(EasingFunctionProperty);
+ set => SetValue(EasingFunctionProperty, value);
+ }
+
+ public bool IsAdditive
+ {
+ get => (bool)GetValue(IsAdditiveProperty);
+ set => SetValue(IsAdditiveProperty, value);
+ }
+
+ public bool IsCumulative
+ {
+ get => (bool)GetValue(IsCumulativeProperty);
+ set => SetValue(IsCumulativeProperty, value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationBase.cs b/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationBase.cs
new file mode 100644
index 00000000..0032e9b2
--- /dev/null
+++ b/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationBase.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+
+namespace HandyControl.Media.Animation
+{
+ public abstract class GeometryAnimationBase : AnimationTimeline
+ {
+ public new GeometryAnimationBase Clone()
+ {
+ return (GeometryAnimationBase)base.Clone();
+ }
+
+ public sealed override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
+ {
+ if (defaultOriginValue == null)
+ {
+ throw new ArgumentNullException(nameof(defaultOriginValue));
+ }
+ if (defaultDestinationValue == null)
+ {
+ throw new ArgumentNullException(nameof(defaultDestinationValue));
+ }
+ return GetCurrentValue((Geometry)defaultOriginValue, (Geometry)defaultDestinationValue, animationClock);
+ }
+
+ public override Type TargetPropertyType
+ {
+ get
+ {
+ ReadPreamble();
+
+ return typeof(Geometry);
+ }
+ }
+
+ public Geometry GetCurrentValue(Geometry defaultOriginValue, Geometry defaultDestinationValue, AnimationClock animationClock)
+ {
+ ReadPreamble();
+
+ if (animationClock == null)
+ {
+ throw new ArgumentNullException(nameof(animationClock));
+ }
+
+ if (animationClock.CurrentState == ClockState.Stopped)
+ {
+ return defaultDestinationValue;
+ }
+
+ return GetCurrentValueCore(defaultOriginValue, defaultDestinationValue, animationClock);
+ }
+
+ protected abstract Geometry GetCurrentValueCore(Geometry defaultOriginValue, Geometry defaultDestinationValue, AnimationClock animationClock);
+ }
+}
\ No newline at end of file
diff --git a/src/Shared/HandyControl_Shared/Properties/AssemblyInfo.cs b/src/Shared/HandyControl_Shared/Properties/AssemblyInfo.cs
index 8c3bfb5e..3e1281fb 100644
--- a/src/Shared/HandyControl_Shared/Properties/AssemblyInfo.cs
+++ b/src/Shared/HandyControl_Shared/Properties/AssemblyInfo.cs
@@ -21,6 +21,7 @@ using System.Runtime.InteropServices;
[assembly: XmlnsDefinition("https://handyorg.github.io/handycontrol", "HandyControl.Interactivity")]
[assembly: XmlnsDefinition("https://handyorg.github.io/handycontrol", "HandyControl.Expression.Shapes")]
[assembly: XmlnsDefinition("https://handyorg.github.io/handycontrol", "HandyControl.Expression.Media")]
+[assembly: XmlnsDefinition("https://handyorg.github.io/handycontrol", "HandyControl.Media.Animation")]
[assembly: XmlnsDefinition("https://handyorg.github.io/handycontrol", "HandyControl.Data")]
[assembly: XmlnsDefinition("https://handyorg.github.io/handycontrol", "HandyControl.Properties.Langs")]
[assembly: XmlnsPrefix("https://handyorg.github.io/handycontrol", "hc")]
\ No newline at end of file
diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs
index feeb9cce..b1004d67 100644
--- a/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs
+++ b/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs
@@ -15,7 +15,7 @@ namespace HandyControl.Tools
///
///
///
- public static ThicknessAnimation CreateAnimation(Thickness thickness = default(Thickness), double milliseconds = 200)
+ public static ThicknessAnimation CreateAnimation(Thickness thickness = default, double milliseconds = 200)
{
return new ThicknessAnimation(thickness, new Duration(TimeSpan.FromMilliseconds(milliseconds)))
{