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 01/10] 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))) { From 646f983228d6553e322548f5c45e04e8ba4118e7 Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Thu, 26 Sep 2019 21:05:30 +0800 Subject: [PATCH 02/10] Update GeometryAnimation.cs --- .../Media/Animation/GeometryAnimation.cs | 53 +++++++------------ 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimation.cs b/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimation.cs index 85c818e4..7446d679 100644 --- a/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimation.cs +++ b/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimation.cs @@ -20,10 +20,6 @@ namespace HandyControl.Media.Animation private double[] _numbersAccumulator; - private double[][] _keyValues; - - private bool _isAnimationFunctionValid; - public GeometryAnimation() { @@ -99,11 +95,26 @@ namespace HandyControl.Media.Animation protected override Geometry GetCurrentValueCore(Geometry defaultOriginValue, Geometry defaultDestinationValue, AnimationClock animationClock) { - if (!_isAnimationFunctionValid) + if (_numbersAccumulator == null) { - ValidateAnimationFunction(); + if (_numbersFrom == null) + { + var geometryStr = defaultOriginValue.ToString(); + DecomposeValue(geometryStr, out _numbersFrom); + } + + if (_numbersTo == null) + { + var geometryStr = defaultDestinationValue.ToString(); + DecomposeValue(geometryStr, out _numbersTo); + _strings = Regex.Split(geometryStr, Rgx); + } + + UpdateValue(); } + if (_numbersAccumulator == null) return defaultOriginValue; + var progress = animationClock.CurrentProgress.Value; var easingFunction = EasingFunction; @@ -113,16 +124,7 @@ namespace HandyControl.Media.Animation } 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); @@ -141,23 +143,12 @@ namespace HandyControl.Media.Animation for (var i = 0; i < _numbersAccumulator.Length; i++) { - numbers[i] = foundation[i] + accumulated[i] + from[i] + _numbersAccumulator[i] * progress; + numbers[i] = accumulated[i] + _numbersFrom[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)); @@ -207,12 +198,6 @@ namespace HandyControl.Media.Animation set => SetValue(EasingFunctionProperty, value); } - public bool IsAdditive - { - get => (bool)GetValue(IsAdditiveProperty); - set => SetValue(IsAdditiveProperty, value); - } - public bool IsCumulative { get => (bool)GetValue(IsCumulativeProperty); From a2e719925dfe553725d4f6389664c7249cfcc9ce Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Thu, 26 Sep 2019 21:31:59 +0800 Subject: [PATCH 03/10] Create GeometryKeyFrame.cs --- .../Media/Animation/GeometryKeyFrame.cs | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs diff --git a/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs b/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs new file mode 100644 index 00000000..fca1fe0a --- /dev/null +++ b/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs @@ -0,0 +1,54 @@ +using System; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Animation; + +public abstract class GeometryKeyFrame : Freezable, IKeyFrame +{ + protected GeometryKeyFrame(Geometry value) + { + Value = value; + } + + protected GeometryKeyFrame(Geometry value, KeyTime keyTime) + { + Value = value; + KeyTime = keyTime; + } + + public static readonly DependencyProperty KeyTimeProperty = DependencyProperty.Register( + "KeyTime", typeof(KeyTime), typeof(GeometryKeyFrame), new PropertyMetadata(KeyTime.Uniform)); + + public KeyTime KeyTime + { + get => (KeyTime) GetValue(KeyTimeProperty); + set => SetValue(KeyTimeProperty, value); + } + + object IKeyFrame.Value + { + get => Value; + set => Value = (Geometry)value; + } + + public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( + "Value", typeof(Geometry), typeof(GeometryKeyFrame), new PropertyMetadata(default(Geometry))); + + public Geometry Value + { + get => (Geometry) GetValue(ValueProperty); + set => SetValue(ValueProperty, value); + } + + public Geometry InterpolateValue(Geometry baseValue, double keyFrameProgress) + { + if (keyFrameProgress < 0.0 || keyFrameProgress > 1.0) + { + throw new ArgumentOutOfRangeException(nameof(keyFrameProgress)); + } + + return InterpolateValueCore(baseValue, keyFrameProgress); + } + + protected abstract Geometry InterpolateValueCore(Geometry baseValue, double keyFrameProgress); +} \ No newline at end of file From 8fa4586c8a7d8cd808784299f210c277ea5b7b1c Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Thu, 26 Sep 2019 21:39:04 +0800 Subject: [PATCH 04/10] add DiscreteGeometryKeyFrame --- .../Animation/DiscreteGeometryKeyFrame.cs | 22 +++++++++++++++++++ .../Media/Animation/GeometryKeyFrame.cs | 5 +++++ 2 files changed, 27 insertions(+) create mode 100644 src/Shared/HandyControl_Shared/Media/Animation/DiscreteGeometryKeyFrame.cs diff --git a/src/Shared/HandyControl_Shared/Media/Animation/DiscreteGeometryKeyFrame.cs b/src/Shared/HandyControl_Shared/Media/Animation/DiscreteGeometryKeyFrame.cs new file mode 100644 index 00000000..047e4c5f --- /dev/null +++ b/src/Shared/HandyControl_Shared/Media/Animation/DiscreteGeometryKeyFrame.cs @@ -0,0 +1,22 @@ +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Animation; + +public class DiscreteGeometryKeyFrame : GeometryKeyFrame +{ + public DiscreteGeometryKeyFrame() + { + } + + public DiscreteGeometryKeyFrame(Geometry value) : base(value) + { + } + + public DiscreteGeometryKeyFrame(Geometry value, KeyTime keyTime) : base(value, keyTime) + { + } + + protected override Freezable CreateInstanceCore() => new DiscreteGeometryKeyFrame(); + + protected override Geometry InterpolateValueCore(Geometry baseValue, double keyFrameProgress) => keyFrameProgress < 1.0 ? baseValue : Value; +} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs b/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs index fca1fe0a..0f743acc 100644 --- a/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs +++ b/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs @@ -5,6 +5,11 @@ using System.Windows.Media.Animation; public abstract class GeometryKeyFrame : Freezable, IKeyFrame { + protected GeometryKeyFrame() + { + + } + protected GeometryKeyFrame(Geometry value) { Value = value; From 01f43a9ddc6216662bb0796c8c95df1c52445289 Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Thu, 26 Sep 2019 21:58:32 +0800 Subject: [PATCH 05/10] Create GeometryKeyFrameCollection.cs --- .../Animation/GeometryKeyFrameCollection.cs | 323 ++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrameCollection.cs diff --git a/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrameCollection.cs b/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrameCollection.cs new file mode 100644 index 00000000..a65a27e4 --- /dev/null +++ b/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrameCollection.cs @@ -0,0 +1,323 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Windows; + +namespace HandyControl.Media.Animation +{ + public class GeometryKeyFrameCollection : Freezable, IList + { + private List _keyFrames; + + private static GeometryKeyFrameCollection s_emptyCollection; + + public GeometryKeyFrameCollection() + { + _keyFrames = new List(2); + } + + public static GeometryKeyFrameCollection Empty + { + get + { + if (s_emptyCollection == null) + { + var emptyCollection = new GeometryKeyFrameCollection + { + _keyFrames = new List(0) + }; + + emptyCollection.Freeze(); + + s_emptyCollection = emptyCollection; + } + + return s_emptyCollection; + } + } + + public new GeometryKeyFrameCollection Clone() => (GeometryKeyFrameCollection)base.Clone(); + + protected override Freezable CreateInstanceCore() => new GeometryKeyFrameCollection(); + + protected override void CloneCore(Freezable sourceFreezable) + { + var sourceCollection = (GeometryKeyFrameCollection)sourceFreezable; + base.CloneCore(sourceFreezable); + + var count = sourceCollection._keyFrames.Count; + + _keyFrames = new List(count); + + for (var i = 0; i < count; i++) + { + var keyFrame = (GeometryKeyFrame)sourceCollection._keyFrames[i].Clone(); + _keyFrames.Add(keyFrame); + OnFreezablePropertyChanged(null, keyFrame); + } + } + + protected override void CloneCurrentValueCore(Freezable sourceFreezable) + { + var sourceCollection = (GeometryKeyFrameCollection)sourceFreezable; + base.CloneCurrentValueCore(sourceFreezable); + + var count = sourceCollection._keyFrames.Count; + + _keyFrames = new List(count); + + for (var i = 0; i < count; i++) + { + var keyFrame = (GeometryKeyFrame)sourceCollection._keyFrames[i].CloneCurrentValue(); + _keyFrames.Add(keyFrame); + OnFreezablePropertyChanged(null, keyFrame); + } + } + + protected override void GetAsFrozenCore(Freezable sourceFreezable) + { + var sourceCollection = (GeometryKeyFrameCollection)sourceFreezable; + base.GetAsFrozenCore(sourceFreezable); + + var count = sourceCollection._keyFrames.Count; + + _keyFrames = new List(count); + + for (var i = 0; i < count; i++) + { + var keyFrame = (GeometryKeyFrame)sourceCollection._keyFrames[i].GetAsFrozen(); + _keyFrames.Add(keyFrame); + OnFreezablePropertyChanged(null, keyFrame); + } + } + + protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable) + { + var sourceCollection = (GeometryKeyFrameCollection)sourceFreezable; + base.GetCurrentValueAsFrozenCore(sourceFreezable); + + var count = sourceCollection._keyFrames.Count; + + _keyFrames = new List(count); + + for (var i = 0; i < count; i++) + { + var keyFrame = (GeometryKeyFrame)sourceCollection._keyFrames[i].GetCurrentValueAsFrozen(); + _keyFrames.Add(keyFrame); + OnFreezablePropertyChanged(null, keyFrame); + } + } + + protected override bool FreezeCore(bool isChecking) + { + var canFreeze = base.FreezeCore(isChecking); + + for (var i = 0; i < _keyFrames.Count && canFreeze; i++) + { + canFreeze &= Freeze(_keyFrames[i], isChecking); + } + + return canFreeze; + } + + public IEnumerator GetEnumerator() + { + ReadPreamble(); + + return _keyFrames.GetEnumerator(); + } + + void ICollection.CopyTo(Array array, int index) + { + ReadPreamble(); + + ((ICollection)_keyFrames).CopyTo(array, index); + } + + public void CopyTo(GeometryKeyFrame[] array, int index) + { + ReadPreamble(); + + _keyFrames.CopyTo(array, index); + } + + public int Count + { + get + { + ReadPreamble(); + + return _keyFrames.Count; + } + } + + public object SyncRoot + { + get + { + ReadPreamble(); + + return ((ICollection)_keyFrames).SyncRoot; + } + } + + public bool IsSynchronized + { + get + { + ReadPreamble(); + + return IsFrozen || Dispatcher != null; + } + } + + int IList.Add(object keyFrame) => Add((GeometryKeyFrame)keyFrame); + + public int Add(GeometryKeyFrame keyFrame) + { + if (keyFrame == null) + { + throw new ArgumentNullException(nameof(keyFrame)); + } + + WritePreamble(); + + OnFreezablePropertyChanged(null, keyFrame); + _keyFrames.Add(keyFrame); + + WritePostscript(); + + return _keyFrames.Count - 1; + } + + public void Clear() + { + WritePreamble(); + + if (_keyFrames.Count <= 0) return; + + foreach (var frame in _keyFrames) + { + OnFreezablePropertyChanged(frame, null); + } + + _keyFrames.Clear(); + + WritePostscript(); + } + + bool IList.Contains(object keyFrame) => Contains((GeometryKeyFrame)keyFrame); + + public bool Contains(GeometryKeyFrame keyFrame) + { + ReadPreamble(); + + return _keyFrames.Contains(keyFrame); + } + + int IList.IndexOf(object keyFrame) => IndexOf((GeometryKeyFrame)keyFrame); + + public int IndexOf(GeometryKeyFrame keyFrame) + { + ReadPreamble(); + + return _keyFrames.IndexOf(keyFrame); + } + + void IList.Insert(int index, object keyFrame) => Insert(index, (GeometryKeyFrame)keyFrame); + + public void Insert(int index, GeometryKeyFrame keyFrame) + { + if (keyFrame == null) + { + throw new ArgumentNullException(nameof(keyFrame)); + } + + WritePreamble(); + + OnFreezablePropertyChanged(null, keyFrame); + _keyFrames.Insert(index, keyFrame); + + WritePostscript(); + } + + void IList.Remove(object keyFrame) => Remove((GeometryKeyFrame)keyFrame); + + public void Remove(GeometryKeyFrame keyFrame) + { + WritePreamble(); + + if (_keyFrames.Contains(keyFrame)) + { + OnFreezablePropertyChanged(keyFrame, null); + _keyFrames.Remove(keyFrame); + + WritePostscript(); + } + } + + public void RemoveAt(int index) + { + WritePreamble(); + + OnFreezablePropertyChanged(_keyFrames[index], null); + _keyFrames.RemoveAt(index); + + WritePostscript(); + } + + object IList.this[int index] + { + get => this[index]; + set => this[index] = (GeometryKeyFrame)value; + } + + public GeometryKeyFrame this[int index] + { + get + { + ReadPreamble(); + + return _keyFrames[index]; + } + set + { + if (value == null) + { + throw new ArgumentNullException(string.Format(CultureInfo.InvariantCulture, "DoubleKeyFrameCollection[{0}]", index)); + } + + WritePreamble(); + + if (value != _keyFrames[index]) + { + OnFreezablePropertyChanged(_keyFrames[index], value); + _keyFrames[index] = value; + + WritePostscript(); + } + } + } + + public bool IsReadOnly + { + get + { + ReadPreamble(); + + return IsFrozen; + } + } + + public bool IsFixedSize + { + get + { + ReadPreamble(); + + return IsFrozen; + } + } + } +} From 39a1a084c6bc1b2c0a3358d4a7b75a346a410005 Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Thu, 26 Sep 2019 23:06:46 +0800 Subject: [PATCH 06/10] add LinearGeometryKeyFrame --- .../HandyControl_Shared.projitems | 5 ++ .../Media/Animation/GeometryAnimation.cs | 47 +++------------- .../GeometryAnimationUsingKeyFrames.cs | 34 +++++++++++ .../Media/Animation/GeometryKeyFrame.cs | 17 +++--- .../Media/Animation/LinearGeometryKeyFrame.cs | 56 +++++++++++++++++++ .../Tools/Helper/AnimationHelper.cs | 30 ++++++++++ .../Tools/RegularPatterns.cs | 2 +- 7 files changed, 145 insertions(+), 46 deletions(-) create mode 100644 src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationUsingKeyFrames.cs create mode 100644 src/Shared/HandyControl_Shared/Media/Animation/LinearGeometryKeyFrame.cs diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems index 22182e67..58ada875 100644 --- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems +++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems @@ -41,8 +41,13 @@ + + + + + diff --git a/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimation.cs b/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimation.cs index 7446d679..0edd36dd 100644 --- a/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimation.cs +++ b/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimation.cs @@ -1,17 +1,14 @@ -using System.Text; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; using System.Windows; using System.Windows.Media; using System.Windows.Media.Animation; -using HandyControl.Tools.Extension; +using HandyControl.Tools; // 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; @@ -42,16 +39,6 @@ namespace HandyControl.Media.Animation } } - 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; @@ -77,22 +64,6 @@ namespace HandyControl.Media.Animation 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 (_numbersAccumulator == null) @@ -100,14 +71,14 @@ namespace HandyControl.Media.Animation if (_numbersFrom == null) { var geometryStr = defaultOriginValue.ToString(); - DecomposeValue(geometryStr, out _numbersFrom); + AnimationHelper.DecomposeGeometryStr(geometryStr, out _numbersFrom); } if (_numbersTo == null) { var geometryStr = defaultDestinationValue.ToString(); - DecomposeValue(geometryStr, out _numbersTo); - _strings = Regex.Split(geometryStr, Rgx); + AnimationHelper.DecomposeGeometryStr(geometryStr, out _numbersTo); + _strings = Regex.Split(geometryStr, RegularPatterns.DigitsPattern); } UpdateValue(); @@ -146,7 +117,7 @@ namespace HandyControl.Media.Animation numbers[i] = accumulated[i] + _numbersFrom[i] + _numbersAccumulator[i] * progress; } - return ConvertToGeometry(numbers); + return AnimationHelper.ComposeGeometry(_strings, numbers); } public static readonly DependencyProperty FromProperty = DependencyProperty.Register( @@ -157,7 +128,7 @@ namespace HandyControl.Media.Animation var obj = (GeometryAnimation) d; if (e.NewValue is Geometry geometry) { - DecomposeValue(geometry.ToString(), out obj._numbersFrom); + AnimationHelper.DecomposeGeometryStr(geometry.ToString(), out obj._numbersFrom); obj.UpdateValue(); } } @@ -177,8 +148,8 @@ namespace HandyControl.Media.Animation if (e.NewValue is Geometry geometry) { var geometryStr = geometry.ToString(); - DecomposeValue(geometryStr, out obj._numbersTo); - obj._strings = Regex.Split(geometryStr, Rgx); + AnimationHelper.DecomposeGeometryStr(geometryStr, out obj._numbersTo); + obj._strings = Regex.Split(geometryStr, RegularPatterns.DigitsPattern); obj.UpdateValue(); } } diff --git a/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationUsingKeyFrames.cs b/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationUsingKeyFrames.cs new file mode 100644 index 00000000..1405806e --- /dev/null +++ b/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationUsingKeyFrames.cs @@ -0,0 +1,34 @@ +using System.Collections; +using System.Windows; +using System.Windows.Markup; +using System.Windows.Media; +using System.Windows.Media.Animation; + +namespace HandyControl.Media.Animation +{ + [ContentProperty("KeyFrames")] + public class GeometryAnimationUsingKeyFrames : GeometryAnimationBase, IKeyFrameAnimation, IAddChild + { + protected override Freezable CreateInstanceCore() + { + throw new System.NotImplementedException(); + } + + protected override Geometry GetCurrentValueCore(Geometry defaultOriginValue, Geometry defaultDestinationValue, + AnimationClock animationClock) + { + throw new System.NotImplementedException(); + } + + public IList KeyFrames { get; set; } + public void AddChild(object value) + { + throw new System.NotImplementedException(); + } + + public void AddText(string text) + { + throw new System.NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs b/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs index 0f743acc..e0a4efb4 100644 --- a/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs +++ b/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs @@ -2,6 +2,7 @@ using System.Windows; using System.Windows.Media; using System.Windows.Media.Animation; +using HandyControl.Tools; public abstract class GeometryKeyFrame : Freezable, IKeyFrame { @@ -12,12 +13,12 @@ public abstract class GeometryKeyFrame : Freezable, IKeyFrame protected GeometryKeyFrame(Geometry value) { - Value = value; + AnimationHelper.DecomposeGeometryStr(value.ToString(), out var arr); + Value = arr; } - protected GeometryKeyFrame(Geometry value, KeyTime keyTime) + protected GeometryKeyFrame(Geometry value, KeyTime keyTime) : this(value) { - Value = value; KeyTime = keyTime; } @@ -33,18 +34,20 @@ public abstract class GeometryKeyFrame : Freezable, IKeyFrame object IKeyFrame.Value { get => Value; - set => Value = (Geometry)value; + set => Value = (double[])value; } public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( - "Value", typeof(Geometry), typeof(GeometryKeyFrame), new PropertyMetadata(default(Geometry))); + "Value", typeof(Geometry), typeof(GeometryKeyFrame), new PropertyMetadata(default(double[]))); - public Geometry Value + public double[] Value { - get => (Geometry) GetValue(ValueProperty); + get => (double[]) GetValue(ValueProperty); set => SetValue(ValueProperty, value); } + protected string[] Strings { get; set; } + public Geometry InterpolateValue(Geometry baseValue, double keyFrameProgress) { if (keyFrameProgress < 0.0 || keyFrameProgress > 1.0) diff --git a/src/Shared/HandyControl_Shared/Media/Animation/LinearGeometryKeyFrame.cs b/src/Shared/HandyControl_Shared/Media/Animation/LinearGeometryKeyFrame.cs new file mode 100644 index 00000000..af2b2d2a --- /dev/null +++ b/src/Shared/HandyControl_Shared/Media/Animation/LinearGeometryKeyFrame.cs @@ -0,0 +1,56 @@ +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Animation; +using HandyControl.Expression.Drawing; +using HandyControl.Tools; + +namespace HandyControl.Media.Animation +{ + public class LinearGeometryKeyFrame : GeometryKeyFrame + { + private double[] _baseValues; + + public LinearGeometryKeyFrame() + { + + } + + public LinearGeometryKeyFrame(Geometry value) : base(value) + { + + } + + public LinearGeometryKeyFrame(Geometry value, KeyTime keyTime) : base(value, keyTime) + { + + } + + protected override Freezable CreateInstanceCore() => new LinearDoubleKeyFrame(); + + protected override Geometry InterpolateValueCore(Geometry baseValue, double keyFrameProgress) + { + if (_baseValues == null) + { + AnimationHelper.DecomposeGeometryStr(baseValue.ToString(), out _baseValues); + } + + if (MathHelper.IsVerySmall(keyFrameProgress)) + { + return baseValue; + } + + if (MathHelper.AreClose(keyFrameProgress, 1)) + { + return AnimationHelper.ComposeGeometry(Strings, Value); + } + + var accumulated = new double[Value.Length]; + for (var i = 0; i < Value.Length; i++) + { + accumulated[i] = _baseValues[i] + (Value[i] - _baseValues[i]) * keyFrameProgress; + } + + return AnimationHelper.ComposeGeometry(Strings, accumulated); + } + } +} diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs index b1004d67..30851a16 100644 --- a/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs +++ b/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs @@ -1,6 +1,10 @@ using System; +using System.Text; +using System.Text.RegularExpressions; using System.Windows; +using System.Windows.Media; using System.Windows.Media.Animation; +using HandyControl.Tools.Extension; namespace HandyControl.Tools { @@ -36,5 +40,31 @@ namespace HandyControl.Tools EasingFunction = new PowerEase { EasingMode = EasingMode.EaseInOut } }; } + + internal static void DecomposeGeometryStr(string geometryStr, out double[] arr) + { + var collection = Regex.Matches(geometryStr, RegularPatterns.DigitsPattern); + arr = new double[collection.Count]; + for (var i = 0; i < collection.Count; i++) + { + arr[i] = collection[i].Value.Value(); + } + } + + internal static Geometry ComposeGeometry(string[] strings, double[] arr) + { + var builder = new StringBuilder(strings[0]); + for (var i = 0; i < arr.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()); + } } } \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Tools/RegularPatterns.cs b/src/Shared/HandyControl_Shared/Tools/RegularPatterns.cs index 4ff94eb6..7226a6b0 100644 --- a/src/Shared/HandyControl_Shared/Tools/RegularPatterns.cs +++ b/src/Shared/HandyControl_Shared/Tools/RegularPatterns.cs @@ -89,7 +89,7 @@ /// /// 计算性质数字正则匹配 /// - public const string DigitsPattern = @"^[+-]?([0-9]*\.?[0-9]+|[0-9]+\.?[0-9]*)([eE][+-]?[0-9]+)?$"; + public const string DigitsPattern = @"[+-]?\d*\.?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?"; /// /// 正整数正则匹配 From f747e207d1c77620e3dd1b221b155139848d80f6 Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Thu, 26 Sep 2019 23:20:05 +0800 Subject: [PATCH 07/10] add EasingGeometryKeyFrame --- .../HandyControl_Shared.projitems | 1 + .../Media/Animation/EasingGeometryKeyFrame.cs | 70 +++++++++++++++++++ .../Media/Animation/LinearGeometryKeyFrame.cs | 10 +-- .../Tools/Helper/AnimationHelper.cs | 12 ++++ 4 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 src/Shared/HandyControl_Shared/Media/Animation/EasingGeometryKeyFrame.cs diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems index 58ada875..aa3143e8 100644 --- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems +++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems @@ -42,6 +42,7 @@ + diff --git a/src/Shared/HandyControl_Shared/Media/Animation/EasingGeometryKeyFrame.cs b/src/Shared/HandyControl_Shared/Media/Animation/EasingGeometryKeyFrame.cs new file mode 100644 index 00000000..2bba2835 --- /dev/null +++ b/src/Shared/HandyControl_Shared/Media/Animation/EasingGeometryKeyFrame.cs @@ -0,0 +1,70 @@ +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Animation; +using HandyControl.Expression.Drawing; +using HandyControl.Tools; + +namespace HandyControl.Media.Animation +{ + public class EasingGeometryKeyFrame : GeometryKeyFrame + { + private double[] _baseValues; + + public EasingGeometryKeyFrame() + { + + } + + public EasingGeometryKeyFrame(Geometry value) : base(value) + { + + } + + public EasingGeometryKeyFrame(Geometry value, KeyTime keyTime) : base(value, keyTime) + { + + } + + public EasingGeometryKeyFrame(Geometry value, KeyTime keyTime, IEasingFunction easingFunction) : base(value, keyTime) + { + EasingFunction = easingFunction; + } + + protected override Freezable CreateInstanceCore() => new EasingGeometryKeyFrame(); + + protected override Geometry InterpolateValueCore(Geometry baseValue, double keyFrameProgress) + { + if (_baseValues == null) + { + AnimationHelper.DecomposeGeometryStr(baseValue.ToString(), out _baseValues); + } + + var easingFunction = EasingFunction; + if (easingFunction != null) + { + keyFrameProgress = easingFunction.Ease(keyFrameProgress); + } + + if (MathHelper.IsVerySmall(keyFrameProgress)) + { + return baseValue; + } + + if (MathHelper.AreClose(keyFrameProgress, 1)) + { + return AnimationHelper.ComposeGeometry(Strings, Value); + } + + return AnimationHelper.InterpolateGeometry(_baseValues, Value, keyFrameProgress, Strings); + } + + public static readonly DependencyProperty EasingFunctionProperty = DependencyProperty.Register( + "EasingFunction", typeof(IEasingFunction), typeof(EasingGeometryKeyFrame), new PropertyMetadata(default(IEasingFunction))); + + public IEasingFunction EasingFunction + { + get => (IEasingFunction) GetValue(EasingFunctionProperty); + set => SetValue(EasingFunctionProperty, value); + } + } +} diff --git a/src/Shared/HandyControl_Shared/Media/Animation/LinearGeometryKeyFrame.cs b/src/Shared/HandyControl_Shared/Media/Animation/LinearGeometryKeyFrame.cs index af2b2d2a..a6ad8cb4 100644 --- a/src/Shared/HandyControl_Shared/Media/Animation/LinearGeometryKeyFrame.cs +++ b/src/Shared/HandyControl_Shared/Media/Animation/LinearGeometryKeyFrame.cs @@ -25,7 +25,7 @@ namespace HandyControl.Media.Animation } - protected override Freezable CreateInstanceCore() => new LinearDoubleKeyFrame(); + protected override Freezable CreateInstanceCore() => new LinearGeometryKeyFrame(); protected override Geometry InterpolateValueCore(Geometry baseValue, double keyFrameProgress) { @@ -44,13 +44,7 @@ namespace HandyControl.Media.Animation return AnimationHelper.ComposeGeometry(Strings, Value); } - var accumulated = new double[Value.Length]; - for (var i = 0; i < Value.Length; i++) - { - accumulated[i] = _baseValues[i] + (Value[i] - _baseValues[i]) * keyFrameProgress; - } - - return AnimationHelper.ComposeGeometry(Strings, accumulated); + return AnimationHelper.InterpolateGeometry(_baseValues, Value, keyFrameProgress, Strings); } } } diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs index 30851a16..1a46a868 100644 --- a/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs +++ b/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs @@ -66,5 +66,17 @@ namespace HandyControl.Tools return Geometry.Parse(builder.ToString()); } + + internal static Geometry InterpolateGeometry(double[] from, double[] to, double progress, string[] strings) + { + var accumulated = new double[to.Length]; + for (var i = 0; i < to.Length; i++) + { + var fromValue = from[i]; + accumulated[i] = fromValue + (to[i] - fromValue) * progress; + } + + return ComposeGeometry(strings, accumulated); + } } } \ No newline at end of file From f549793e13272eb215d00b01536a49a4acadac5d Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Thu, 26 Sep 2019 23:26:46 +0800 Subject: [PATCH 08/10] add SplineGeometryKeyFrame --- .../HandyControl_Shared.projitems | 1 + .../Media/Animation/SplineGeometryKeyFrame.cs | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/Shared/HandyControl_Shared/Media/Animation/SplineGeometryKeyFrame.cs diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems index aa3143e8..82d0c876 100644 --- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems +++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems @@ -49,6 +49,7 @@ + diff --git a/src/Shared/HandyControl_Shared/Media/Animation/SplineGeometryKeyFrame.cs b/src/Shared/HandyControl_Shared/Media/Animation/SplineGeometryKeyFrame.cs new file mode 100644 index 00000000..d501c2db --- /dev/null +++ b/src/Shared/HandyControl_Shared/Media/Animation/SplineGeometryKeyFrame.cs @@ -0,0 +1,66 @@ +using System; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Animation; +using HandyControl.Expression.Drawing; +using HandyControl.Tools; + +namespace HandyControl.Media.Animation +{ + public class SplineGeometryKeyFrame : GeometryKeyFrame + { + private double[] _baseValues; + + public SplineGeometryKeyFrame() + { + + } + + public SplineGeometryKeyFrame(Geometry value) : base(value) + { + + } + + public SplineGeometryKeyFrame(Geometry value, KeyTime keyTime) : base(value, keyTime) + { + + } + + public SplineGeometryKeyFrame(Geometry value, KeyTime keyTime, KeySpline keySpline) : base(value, keyTime) + { + KeySpline = keySpline ?? throw new ArgumentNullException(nameof(keySpline)); + } + + protected override Freezable CreateInstanceCore() => new SplineGeometryKeyFrame(); + + protected override Geometry InterpolateValueCore(Geometry baseValue, double keyFrameProgress) + { + if (_baseValues == null) + { + AnimationHelper.DecomposeGeometryStr(baseValue.ToString(), out _baseValues); + } + + if (MathHelper.IsVerySmall(keyFrameProgress)) + { + return baseValue; + } + + if (MathHelper.AreClose(keyFrameProgress, 1)) + { + return AnimationHelper.ComposeGeometry(Strings, Value); + } + + var splineProgress = KeySpline.GetSplineProgress(keyFrameProgress); + return AnimationHelper.InterpolateGeometry(_baseValues, Value, splineProgress, Strings); + } + + public static readonly DependencyProperty KeySplineProperty = DependencyProperty.Register( + "KeySpline", typeof(KeySpline), typeof(SplineGeometryKeyFrame), new PropertyMetadata(new KeySpline())); + + public KeySpline KeySpline + { + get => (KeySpline) GetValue(KeySplineProperty); + set => SetValue(KeySplineProperty, value); + } + } +} From 0e74e5b34ee19eff2cc431ef780646cf7893b6e0 Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Fri, 27 Sep 2019 23:51:30 +0800 Subject: [PATCH 09/10] add GeometryAnimationUsingKeyFrames --- .../Resources/Themes/Basic/Geometries.xaml | 2 + .../HandyControl_Shared.projitems | 1 + .../Animation/DiscreteGeometryKeyFrame.cs | 31 +- .../Media/Animation/EasingGeometryKeyFrame.cs | 13 +- .../Media/Animation/GeometryAnimationBase.cs | 5 +- .../GeometryAnimationUsingKeyFrames.cs | 506 +++++++++++++++++- .../Media/Animation/GeometryKeyFrame.cs | 32 +- .../Animation/GeometryKeyFrameCollection.cs | 2 +- .../Media/Animation/LinearGeometryKeyFrame.cs | 13 +- .../Media/Animation/ResolvedKeyFrameEntry.cs | 38 ++ .../Media/Animation/SplineGeometryKeyFrame.cs | 13 +- .../Tools/Helper/AnimationHelper.cs | 12 + 12 files changed, 595 insertions(+), 73 deletions(-) create mode 100644 src/Shared/HandyControl_Shared/Media/Animation/ResolvedKeyFrameEntry.cs diff --git a/src/Shared/HandyControlDemo_Shared/Resources/Themes/Basic/Geometries.xaml b/src/Shared/HandyControlDemo_Shared/Resources/Themes/Basic/Geometries.xaml index 0860ffdd..ca18bb48 100644 --- a/src/Shared/HandyControlDemo_Shared/Resources/Themes/Basic/Geometries.xaml +++ b/src/Shared/HandyControlDemo_Shared/Resources/Themes/Basic/Geometries.xaml @@ -9,6 +9,8 @@ M449.722947 199.412211H706.021053c130.967579 0 237.136842 106.169263 237.136842 237.136842v256.298105c0 130.967579-106.169263 237.136842-237.136842 237.136842H449.722947c-130.965895 0-237.136842-106.169263-237.136842-237.136842V436.549053c0-130.967579 106.170947-237.136842 237.136842-237.136842z m-201.205894-21.557895c0-64.299789-70.085053-104.704-125.850948-72.55579s-55.764211 112.96 0 145.111579 125.850947-8.257684 125.850948-72.555789z m239.528421 234.74021c0-68.003368-74.088421-110.736842-133.037474-76.734315s-58.950737 119.467789 0 153.470315 133.039158-8.730947 133.039158-76.736zM835.368421 692.847158c0-117.596632-128.154947-191.494737-230.123789-132.695579s-101.970526 206.593684 0 265.391158S835.368421 810.443789 835.368421 692.847158z M430.079734 481.464543c45.196365 45.596478 118.486578 45.596478 163.68499 0l360.684736-324.939627c0.547469 3.751441 1.13894 7.490601 1.13894 11.401678l0 404.753417 0 116.754121c0 42.995233-34.547841 77.838809-77.145008 77.838809L145.397996 767.272941c-42.597167 0-77.145008-34.844599-77.145008-77.838809L68.252988 494.839155 68.252988 167.926594c0-3.911076 0.593518-7.65126 1.139963-11.401678L430.079734 481.464543zM910.960992 74.226538 562.044278 387.28558c-38.470173 38.811957-62.276369 38.811957-100.770078 0L112.538611 74.408687c10.011005-4.789073 21.069875-7.673773 32.859385-7.673773l733.044372 0C890.095779 66.735937 901.065622 69.539796 910.960992 74.226538z M260.8 645H160V0h100.8v645zM461.8 152.2h-100.8V1024h100.8V152.2z m201.2 0h-100.8V1024h100.8V152.2zM864 152h-100.8v494H864V152z + m 41.416254,90 c -0.327378,-7.4702 0.20833,-32.7284 0,-39.901 -5.386902,-0.2083 -4.521603,0.3274 -9.848987,0 0.20833,-5.50595 0.36436,-7.66666 0.126269,-13.32142 4.646472,0.0181 3.439989,-0.009 9.848987,-0.1894 0.09586,-3.7736 0.133082,-3.0791 0.126269,-7.38674 0.18259,-3.73943 -0.486609,-10.54308 4.293149,-14.96288 4.779758,-4.4198 13.606811,-3.64808 22.223356,-3.53554 -0.04417,5.73754 -0.03936,9.37986 0,12.87945 -5.049924,0.46388 -7.309188,-0.33689 -10.85914,1.26269 -1.403378,3.17794 -1.569601,4.80531 -1.262691,11.93242 3.147964,-0.13336 8.201788,-0.1378 12.626907,0 -0.995158,6.00899 -0.948285,7.62376 -1.767767,13.06882 -3.676625,0.088 -5.605721,-0.1488 -11.111678,0 -0.148814,6.756 0.357147,33.0107 0,40.1536 -6.428576,0.1786 -8.174438,-0.03 -14.394674,0 z + m 10.44335,90 c 11.073313,0.3952 19.483106,-1.8358 23.901837,-7.1603 -7.9736,-1.4292 -11.832311,-4.1933 -15.078321,-11.0837 3.459698,0.8219 5.795894,0.6358 7.606781,-0.607 -7.19593,-1.719 -12.734543,-6.7971 -13.741664,-15.836 2.766355,1.55307 5.466848,2.66623 7.828682,2.0203 -4.336544,-2.92911 -9.838998,-10.47636 -5.555839,-22.47589 8.400675,11.87052 23.824269,17.67568 33.840111,17.67767 -0.936406,-9.74688 5.88057,-19.46521 15.302849,-19.97853 8.13118,-0.50719 10.57457,4.01944 12.476346,4.82624 3.644547,0.13419 7.393301,-1.74401 10.354063,-3.53553 -1.380842,4.47157 -5.06769,5.62903 -6.313453,8.58629 5.42317,0.41513 5.891376,-1.53111 8.333758,-2.0203 -2.071414,3.75017 -5.393863,5.00034 -7.323606,8.08122 -1.633654,16.12573 -5.16049,27.57123 -14.647212,36.36553 -13.825764,11.3764 -34.755458,17.369 -56.984332,5.14 z M68.191078 915.462005l161.384597 0L229.575676 431.30719 68.191078 431.30719 68.191078 915.462005zM955.808922 471.653083c0-44.582381-36.109406-80.69281-80.69281-80.69281L620.329241 390.960273 658.859789 206.578915c0.807389-4.034896 1.412163-8.271384 1.412163-12.709463 0-16.743336-6.859221-31.873941-17.752316-42.767036l-42.968627-42.565445L333.871043 374.216937c-14.524808 14.7264-23.602557 34.899858-23.602557 57.090253l0 403.462005c0 44.582381 36.109406 80.69281 80.69281 80.69281l363.116111 0c33.487695 0 62.133106-20.37505 74.236771-49.222051l121.643478-284.441261c3.63069-9.279341 5.850242-19.164478 5.850242-29.452799L955.807898 475.083206l-0.403183-0.403183L955.808922 471.653083z M736 224H288a64 64 0 0 0-64 64v448a64 64 0 0 0 64 64h448a64 64 0 0 0 64-64V288a64 64 0 0 0-64-64z m-416 160a64 64 0 1 1 128.032 0.032A64 64 0 0 1 320 384z m416 320a32 32 0 0 1-32 32H384a32 32 0 0 1-32-32l96-160c26.432-2.048 68.128 6.016 128 64l128-224a32 32 0 0 1 32 32v288z M19.692308 512A25 25 0 1 1 19.692308 531.692308ZM512 303.027832C512 286.548884 524.86564 273.174666 540.717948 273.174666L598.153846 273.174666C614.006154 273.174666 626.871794 286.548884 626.871794 303.027832L626.871794 362.734167C626.871794 379.213115 614.006154 392.587333 598.153846 392.587333L540.717948 392.587333C524.86564 392.587333 512 379.213115 512 362.734167L512 303.027832 512 303.027832ZM512 482.146834 512 541.853166C512 558.332115 499.13436 571.706335 483.282052 571.706335L425.846154 571.706335C409.993846 571.706335 397.128206 558.332115 397.128206 541.853166L397.128206 482.146834C397.128206 465.667885 409.993846 452.293665 425.846154 452.293665L483.282052 452.293665C499.13436 452.293665 512 465.667885 512 482.146834L512 482.146834ZM339.692308 303.027832C339.692308 286.548884 352.557948 273.174666 368.410256 273.174666L425.846154 273.174666C441.698462 273.174666 454.564102 286.548884 454.564102 303.027832L454.564102 362.734167C454.564102 379.213115 441.698462 392.587333 425.846154 392.587333L368.410256 392.587333C352.557948 392.587333 339.692308 379.213115 339.692308 362.734167L339.692308 303.027832 339.692308 303.027832ZM167.384615 303.027832C167.384615 286.548884 180.250256 273.174666 196.102564 273.174666L253.538462 273.174666C269.390769 273.174666 282.25641 286.548884 282.25641 303.027832L282.25641 362.734167C282.25641 379.213115 269.390769 392.587333 253.538462 392.587333L196.102564 392.587333C180.250256 392.587333 167.384615 379.213115 167.384615 362.734167L167.384615 303.027832 167.384615 303.027832ZM282.25641 720.972168C282.25641 737.451116 269.390769 750.825334 253.538462 750.825334L196.102564 750.825334C180.250256 750.825334 167.384615 737.451116 167.384615 720.972168L167.384615 661.265833C167.384615 644.786885 180.250256 631.412667 196.102564 631.412667L253.538462 631.412667C269.390769 631.412667 282.25641 644.786885 282.25641 661.265833L282.25641 720.972168 282.25641 720.972168ZM310.97436 571.706335 253.538462 571.706335C237.686154 571.706335 224.820513 558.332115 224.820513 541.853166L224.820513 482.146834C224.820513 465.667885 237.686154 452.293665 253.538462 452.293665L310.97436 452.293665C326.826667 452.293665 339.692308 465.667885 339.692308 482.146834L339.692308 541.853166C339.692308 558.332115 326.826667 571.706335 310.97436 571.706335L310.97436 571.706335ZM569.435898 720.972168C569.435898 737.451116 556.570256 750.825334 540.717948 750.825334L368.410256 750.825334C352.557948 750.825334 339.692308 737.451116 339.692308 720.972168L339.692308 661.265833C339.692308 644.786885 352.557948 631.412667 368.410256 631.412667L540.717948 631.412667C556.570256 631.412667 569.435898 644.786885 569.435898 661.265833L569.435898 720.972168 569.435898 720.972168ZM569.435898 541.853166 569.435898 482.146834C569.435898 465.667885 582.301538 452.293665 598.153846 452.293665L655.589744 452.293665C671.442052 452.293665 684.307692 465.667885 684.307692 482.146834L684.307692 541.853166C684.307692 558.332115 671.442052 571.706335 655.589744 571.706335L598.153846 571.706335C582.301538 571.706335 569.435898 558.332115 569.435898 541.853166L569.435898 541.853166ZM856.615385 720.972168C856.615385 737.451116 843.749744 750.825334 827.897437 750.825334L655.589744 750.825334C639.737437 750.825334 626.871794 737.451116 626.871794 720.972168L626.871794 661.265833C626.871794 644.786885 639.737437 631.412667 655.589744 631.412667L713.02564 631.412667C728.877948 631.412667 741.74359 618.038449 741.74359 601.559501L741.74359 482.146834C741.74359 465.667885 754.609231 452.293665 770.461538 452.293665L827.897437 452.293665C843.749744 452.293665 856.615385 465.667885 856.615385 482.146834L856.615385 720.972168 856.615385 720.972168ZM856.615385 362.734167C856.615385 379.213115 843.749744 392.587333 827.897437 392.587333L713.02564 392.587333C697.173333 392.587333 684.307692 379.213115 684.307692 362.734167L684.307692 303.027832C684.307692 286.548884 697.173333 273.174666 713.02564 273.174666L827.897437 273.174666C843.749744 273.174666 856.615385 286.548884 856.615385 303.027832L856.615385 362.734167 856.615385 362.734167Z diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems index 82d0c876..96093d43 100644 --- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems +++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems @@ -49,6 +49,7 @@ + diff --git a/src/Shared/HandyControl_Shared/Media/Animation/DiscreteGeometryKeyFrame.cs b/src/Shared/HandyControl_Shared/Media/Animation/DiscreteGeometryKeyFrame.cs index 047e4c5f..2a228e9f 100644 --- a/src/Shared/HandyControl_Shared/Media/Animation/DiscreteGeometryKeyFrame.cs +++ b/src/Shared/HandyControl_Shared/Media/Animation/DiscreteGeometryKeyFrame.cs @@ -2,21 +2,24 @@ using System.Windows.Media; using System.Windows.Media.Animation; -public class DiscreteGeometryKeyFrame : GeometryKeyFrame +namespace HandyControl.Media.Animation { - public DiscreteGeometryKeyFrame() + public class DiscreteGeometryKeyFrame : GeometryKeyFrame { + public DiscreteGeometryKeyFrame() + { + } + + public DiscreteGeometryKeyFrame(Geometry value) : base(value) + { + } + + public DiscreteGeometryKeyFrame(Geometry value, KeyTime keyTime) : base(value, keyTime) + { + } + + protected override Freezable CreateInstanceCore() => new DiscreteGeometryKeyFrame(); + + protected override double[] InterpolateValueCore(double[] baseValue, double keyFrameProgress) => keyFrameProgress < 1.0 ? baseValue : Numbers; } - - public DiscreteGeometryKeyFrame(Geometry value) : base(value) - { - } - - public DiscreteGeometryKeyFrame(Geometry value, KeyTime keyTime) : base(value, keyTime) - { - } - - protected override Freezable CreateInstanceCore() => new DiscreteGeometryKeyFrame(); - - protected override Geometry InterpolateValueCore(Geometry baseValue, double keyFrameProgress) => keyFrameProgress < 1.0 ? baseValue : Value; } \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Media/Animation/EasingGeometryKeyFrame.cs b/src/Shared/HandyControl_Shared/Media/Animation/EasingGeometryKeyFrame.cs index 2bba2835..7c70bef3 100644 --- a/src/Shared/HandyControl_Shared/Media/Animation/EasingGeometryKeyFrame.cs +++ b/src/Shared/HandyControl_Shared/Media/Animation/EasingGeometryKeyFrame.cs @@ -8,8 +8,6 @@ namespace HandyControl.Media.Animation { public class EasingGeometryKeyFrame : GeometryKeyFrame { - private double[] _baseValues; - public EasingGeometryKeyFrame() { @@ -32,13 +30,8 @@ namespace HandyControl.Media.Animation protected override Freezable CreateInstanceCore() => new EasingGeometryKeyFrame(); - protected override Geometry InterpolateValueCore(Geometry baseValue, double keyFrameProgress) + protected override double[] InterpolateValueCore(double[] baseValue, double keyFrameProgress) { - if (_baseValues == null) - { - AnimationHelper.DecomposeGeometryStr(baseValue.ToString(), out _baseValues); - } - var easingFunction = EasingFunction; if (easingFunction != null) { @@ -52,10 +45,10 @@ namespace HandyControl.Media.Animation if (MathHelper.AreClose(keyFrameProgress, 1)) { - return AnimationHelper.ComposeGeometry(Strings, Value); + return Numbers; } - return AnimationHelper.InterpolateGeometry(_baseValues, Value, keyFrameProgress, Strings); + return AnimationHelper.InterpolateGeometryValue(baseValue, Numbers, keyFrameProgress); } public static readonly DependencyProperty EasingFunctionProperty = DependencyProperty.Register( diff --git a/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationBase.cs b/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationBase.cs index 0032e9b2..eee8c262 100644 --- a/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationBase.cs +++ b/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationBase.cs @@ -6,10 +6,7 @@ namespace HandyControl.Media.Animation { public abstract class GeometryAnimationBase : AnimationTimeline { - public new GeometryAnimationBase Clone() - { - return (GeometryAnimationBase)base.Clone(); - } + public new GeometryAnimationBase Clone() => (GeometryAnimationBase)base.Clone(); public sealed override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock) { diff --git a/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationUsingKeyFrames.cs b/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationUsingKeyFrames.cs index 1405806e..38b89150 100644 --- a/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationUsingKeyFrames.cs +++ b/src/Shared/HandyControl_Shared/Media/Animation/GeometryAnimationUsingKeyFrames.cs @@ -1,34 +1,518 @@ -using System.Collections; +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text.RegularExpressions; using System.Windows; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Media.Animation; +using HandyControl.Tools; + +// ReSharper disable PossibleInvalidOperationException namespace HandyControl.Media.Animation { [ContentProperty("KeyFrames")] public class GeometryAnimationUsingKeyFrames : GeometryAnimationBase, IKeyFrameAnimation, IAddChild { - protected override Freezable CreateInstanceCore() + private string[] _strings; + + public string[] Strings { - throw new System.NotImplementedException(); + get + { + if (_keyFrames == null || _keyFrames.Count == 0) + { + return null; + } + + return _strings ??= Regex.Split(_keyFrames[0].Value.ToString(), RegularPatterns.DigitsPattern); + } } - protected override Geometry GetCurrentValueCore(Geometry defaultOriginValue, Geometry defaultDestinationValue, - AnimationClock animationClock) + private GeometryKeyFrameCollection _keyFrames; + + private ResolvedKeyFrameEntry[] _sortedResolvedKeyFrames; + + private bool _areKeyTimesValid; + + public GeometryAnimationUsingKeyFrames() => _areKeyTimesValid = true; + + public new GeometryAnimationUsingKeyFrames Clone() => (GeometryAnimationUsingKeyFrames)base.Clone(); + + public new GeometryAnimationUsingKeyFrames CloneCurrentValue() => (GeometryAnimationUsingKeyFrames)base.CloneCurrentValue(); + + protected override bool FreezeCore(bool isChecking) { - throw new System.NotImplementedException(); + var canFreeze = base.FreezeCore(isChecking); + + canFreeze &= Freeze(_keyFrames, isChecking); + + if (canFreeze & !_areKeyTimesValid) + { + ResolveKeyTimes(); + } + + return canFreeze; } - public IList KeyFrames { get; set; } - public void AddChild(object value) + protected override void OnChanged() { - throw new System.NotImplementedException(); + _areKeyTimesValid = false; + + base.OnChanged(); } - public void AddText(string text) + protected override Freezable CreateInstanceCore() => new GeometryAnimationUsingKeyFrames(); + + protected override void CloneCore(Freezable sourceFreezable) { - throw new System.NotImplementedException(); + var sourceAnimation = (GeometryAnimationUsingKeyFrames)sourceFreezable; + base.CloneCore(sourceFreezable); + + CopyCommon(sourceAnimation, false); + } + + protected override void CloneCurrentValueCore(Freezable sourceFreezable) + { + var sourceAnimation = (GeometryAnimationUsingKeyFrames)sourceFreezable; + base.CloneCurrentValueCore(sourceFreezable); + + CopyCommon(sourceAnimation, true); + } + + protected override void GetAsFrozenCore(Freezable source) + { + var sourceAnimation = (GeometryAnimationUsingKeyFrames)source; + base.GetAsFrozenCore(source); + + CopyCommon(sourceAnimation, false); + } + + protected override void GetCurrentValueAsFrozenCore(Freezable source) + { + var sourceAnimation = (GeometryAnimationUsingKeyFrames)source; + base.GetCurrentValueAsFrozenCore(source); + + CopyCommon(sourceAnimation, true); + } + + private void CopyCommon(GeometryAnimationUsingKeyFrames sourceAnimation, bool isCurrentValueClone) + { + _areKeyTimesValid = sourceAnimation._areKeyTimesValid; + + if (_areKeyTimesValid && sourceAnimation._sortedResolvedKeyFrames != null) + { + _sortedResolvedKeyFrames = (ResolvedKeyFrameEntry[])sourceAnimation._sortedResolvedKeyFrames.Clone(); + } + + if (sourceAnimation._keyFrames != null) + { + if (isCurrentValueClone) + { + _keyFrames = (GeometryKeyFrameCollection)sourceAnimation._keyFrames.CloneCurrentValue(); + } + else + { + _keyFrames = sourceAnimation._keyFrames.Clone(); + } + + OnFreezablePropertyChanged(null, _keyFrames); + } + } + + void IAddChild.AddChild(object child) + { + WritePreamble(); + + if (child == null) + { + throw new ArgumentNullException(nameof(child)); + } + + AddChild(child); + + WritePostscript(); + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void AddChild(object child) + { + if (child is GeometryKeyFrame keyFrame) + { + KeyFrames.Add(keyFrame); + } + else + { + throw new ArgumentException("Animation_ChildMustBeKeyFrame", nameof(child)); + } + } + + void IAddChild.AddText(string childText) + { + if (childText == null) + { + throw new ArgumentNullException(nameof(childText)); + } + + AddText(childText); + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void AddText(string childText) => throw new InvalidOperationException("Animation_NoTextChildren"); + + protected override Geometry GetCurrentValueCore(Geometry defaultOriginValue, Geometry defaultDestinationValue, AnimationClock animationClock) + { + if (_keyFrames == null) + { + return defaultDestinationValue; + } + + if (!_areKeyTimesValid) + { + ResolveKeyTimes(); + } + + if (_sortedResolvedKeyFrames == null) + { + return defaultDestinationValue; + } + + var currentTime = animationClock.CurrentTime.Value; + var keyFrameCount = _sortedResolvedKeyFrames.Length; + var maxKeyFrameIndex = keyFrameCount - 1; + + double[] currentIterationValue; + + var currentResolvedKeyFrameIndex = 0; + + while (currentResolvedKeyFrameIndex < keyFrameCount && currentTime > _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex]._resolvedKeyTime) + { + currentResolvedKeyFrameIndex++; + } + + while (currentResolvedKeyFrameIndex < maxKeyFrameIndex && currentTime == _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex + 1]._resolvedKeyTime) + { + currentResolvedKeyFrameIndex++; + } + + if (currentResolvedKeyFrameIndex == keyFrameCount) + { + currentIterationValue = GetResolvedKeyFrameValue(maxKeyFrameIndex); + } + else if (currentTime == _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex]._resolvedKeyTime) + { + currentIterationValue = GetResolvedKeyFrameValue(currentResolvedKeyFrameIndex); + } + else + { + double currentSegmentProgress; + double[] fromValue; + + if (currentResolvedKeyFrameIndex == 0) + { + AnimationHelper.DecomposeGeometryStr(defaultOriginValue.ToString(), out fromValue); + + currentSegmentProgress = currentTime.TotalMilliseconds / _sortedResolvedKeyFrames[0]._resolvedKeyTime.TotalMilliseconds; + } + else + { + var previousResolvedKeyFrameIndex = currentResolvedKeyFrameIndex - 1; + var previousResolvedKeyTime = _sortedResolvedKeyFrames[previousResolvedKeyFrameIndex]._resolvedKeyTime; + + fromValue = GetResolvedKeyFrameValue(previousResolvedKeyFrameIndex); + + var segmentCurrentTime = currentTime - previousResolvedKeyTime; + var segmentDuration = _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex]._resolvedKeyTime - previousResolvedKeyTime; + + currentSegmentProgress = segmentCurrentTime.TotalMilliseconds / segmentDuration.TotalMilliseconds; + } + + currentIterationValue = GetResolvedKeyFrame(currentResolvedKeyFrameIndex).InterpolateValue(fromValue, currentSegmentProgress); + } + + return AnimationHelper.ComposeGeometry(Strings, currentIterationValue); + } + + protected sealed override Duration GetNaturalDurationCore(Clock clock) => new Duration(LargestTimeSpanKeyTime); + + IList IKeyFrameAnimation.KeyFrames + { + get => KeyFrames; + set => KeyFrames = (GeometryKeyFrameCollection)value; + } + + public GeometryKeyFrameCollection KeyFrames + { + get + { + ReadPreamble(); + + if (_keyFrames == null) + { + if (IsFrozen) + { + _keyFrames = GeometryKeyFrameCollection.Empty; + } + else + { + WritePreamble(); + + _keyFrames = new GeometryKeyFrameCollection(); + + OnFreezablePropertyChanged(null, _keyFrames); + + WritePostscript(); + } + } + + return _keyFrames; + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + WritePreamble(); + + if (value != _keyFrames) + { + OnFreezablePropertyChanged(_keyFrames, value); + _keyFrames = value; + + WritePostscript(); + } + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeKeyFrames() + { + ReadPreamble(); + + return _keyFrames != null && _keyFrames.Count > 0; + } + + private struct KeyTimeBlock + { + public int BeginIndex; + public int EndIndex; + } + + private double[] GetResolvedKeyFrameValue(int resolvedKeyFrameIndex) => + GetResolvedKeyFrame(resolvedKeyFrameIndex).Numbers; + + private GeometryKeyFrame GetResolvedKeyFrame(int resolvedKeyFrameIndex) => + _keyFrames[_sortedResolvedKeyFrames[resolvedKeyFrameIndex]._originalKeyFrameIndex]; + + private TimeSpan LargestTimeSpanKeyTime + { + get + { + var hasTimeSpanKeyTime = false; + var largestTimeSpanKeyTime = TimeSpan.Zero; + + if (_keyFrames != null) + { + var keyFrameCount = _keyFrames.Count; + + for (var index = 0; index < keyFrameCount; index++) + { + var keyTime = _keyFrames[index].KeyTime; + + if (keyTime.Type == KeyTimeType.TimeSpan) + { + hasTimeSpanKeyTime = true; + + if (keyTime.TimeSpan > largestTimeSpanKeyTime) + { + largestTimeSpanKeyTime = keyTime.TimeSpan; + } + } + } + } + + return hasTimeSpanKeyTime ? largestTimeSpanKeyTime : TimeSpan.FromSeconds(1.0); + } + } + + private void ResolveKeyTimes() + { + var keyFrameCount = 0; + + if (_keyFrames != null) + { + keyFrameCount = _keyFrames.Count; + } + + if (keyFrameCount == 0) + { + _sortedResolvedKeyFrames = null; + _areKeyTimesValid = true; + return; + } + + _sortedResolvedKeyFrames = new ResolvedKeyFrameEntry[keyFrameCount]; + + var index = 0; + + for (; index < keyFrameCount; index++) + { + _sortedResolvedKeyFrames[index]._originalKeyFrameIndex = index; + } + + var duration = Duration; + var calculationDuration = duration.HasTimeSpan ? duration.TimeSpan : LargestTimeSpanKeyTime; + var maxKeyFrameIndex = keyFrameCount - 1; + var unspecifiedBlocks = new List(); + var hasPacedKeyTimes = false; + + index = 0; + while (index < keyFrameCount) + { + // ReSharper disable once PossibleNullReferenceException + var keyTime = _keyFrames[index].KeyTime; + + switch (keyTime.Type) + { + case KeyTimeType.Percent: + + _sortedResolvedKeyFrames[index]._resolvedKeyTime = TimeSpan.FromMilliseconds(keyTime.Percent * calculationDuration.TotalMilliseconds); + index++; + break; + + case KeyTimeType.TimeSpan: + + _sortedResolvedKeyFrames[index]._resolvedKeyTime = keyTime.TimeSpan; + + index++; + break; + + case KeyTimeType.Paced: + case KeyTimeType.Uniform: + + if (index == maxKeyFrameIndex) + { + _sortedResolvedKeyFrames[index]._resolvedKeyTime = calculationDuration; + index++; + } + else if (index == 0 && keyTime.Type == KeyTimeType.Paced) + { + _sortedResolvedKeyFrames[index]._resolvedKeyTime = TimeSpan.Zero; + index++; + } + else + { + if (keyTime.Type == KeyTimeType.Paced) + { + hasPacedKeyTimes = true; + } + + var block = new KeyTimeBlock + { + BeginIndex = index + }; + + while (++index < maxKeyFrameIndex) + { + var type = _keyFrames[index].KeyTime.Type; + + if (type == KeyTimeType.Percent || type == KeyTimeType.TimeSpan) + { + break; + } + + if (type == KeyTimeType.Paced) + { + hasPacedKeyTimes = true; + } + } + + block.EndIndex = index; + unspecifiedBlocks.Add(block); + } + + break; + } + } + + foreach (var block in unspecifiedBlocks) + { + var blockBeginTime = TimeSpan.Zero; + + if (block.BeginIndex > 0) + { + blockBeginTime = _sortedResolvedKeyFrames[block.BeginIndex - 1]._resolvedKeyTime; + } + + long segmentCount = block.EndIndex - block.BeginIndex + 1; + var uniformTimeStep = TimeSpan.FromTicks((_sortedResolvedKeyFrames[block.EndIndex]._resolvedKeyTime - blockBeginTime).Ticks / segmentCount); + + index = block.BeginIndex; + var resolvedTime = blockBeginTime + uniformTimeStep; + + while (index < block.EndIndex) + { + _sortedResolvedKeyFrames[index]._resolvedKeyTime = resolvedTime; + + resolvedTime += uniformTimeStep; + index++; + } + } + + if (hasPacedKeyTimes) + { + ResolvePacedKeyTimes(); + } + + Array.Sort(_sortedResolvedKeyFrames); + + _areKeyTimesValid = true; + } + + private void ResolvePacedKeyTimes() + { + var index = 1; + var maxKeyFrameIndex = _sortedResolvedKeyFrames.Length - 1; + + do + { + if (_keyFrames[index].KeyTime.Type == KeyTimeType.Paced) + { + var firstPacedBlockKeyFrameIndex = index; + + var segmentLengths = new List(); + var prePacedBlockKeyTime = _sortedResolvedKeyFrames[index - 1]._resolvedKeyTime; + var totalLength = 0.0; + var prevKeyValue = _keyFrames[index - 1].Numbers; + + do + { + var currentKeyValue = _keyFrames[index].Numbers; + totalLength += Math.Abs(currentKeyValue[0] - prevKeyValue[0]); + segmentLengths.Add(totalLength); + prevKeyValue = currentKeyValue; + index++; + } + while (index < maxKeyFrameIndex && _keyFrames[index].KeyTime.Type == KeyTimeType.Paced); + totalLength += Math.Abs(_keyFrames[index].Numbers[0] - prevKeyValue[0]); + + var pacedBlockDuration = _sortedResolvedKeyFrames[index]._resolvedKeyTime - prePacedBlockKeyTime; + + for (int i = 0, currentKeyFrameIndex = firstPacedBlockKeyFrameIndex; i < segmentLengths.Count; i++, currentKeyFrameIndex++) + { + _sortedResolvedKeyFrames[currentKeyFrameIndex]._resolvedKeyTime = prePacedBlockKeyTime + TimeSpan.FromMilliseconds(segmentLengths[i] / totalLength * pacedBlockDuration.TotalMilliseconds); + } + } + else + { + index++; + } + } + while (index < maxKeyFrameIndex); } } } \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs b/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs index e0a4efb4..8ada4191 100644 --- a/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs +++ b/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrame.cs @@ -6,6 +6,8 @@ using HandyControl.Tools; public abstract class GeometryKeyFrame : Freezable, IKeyFrame { + internal double[] Numbers; + protected GeometryKeyFrame() { @@ -14,13 +16,11 @@ public abstract class GeometryKeyFrame : Freezable, IKeyFrame protected GeometryKeyFrame(Geometry value) { AnimationHelper.DecomposeGeometryStr(value.ToString(), out var arr); - Value = arr; + Numbers = arr; + Value = value; } - protected GeometryKeyFrame(Geometry value, KeyTime keyTime) : this(value) - { - KeyTime = keyTime; - } + protected GeometryKeyFrame(Geometry value, KeyTime keyTime) : this(value) => KeyTime = keyTime; public static readonly DependencyProperty KeyTimeProperty = DependencyProperty.Register( "KeyTime", typeof(KeyTime), typeof(GeometryKeyFrame), new PropertyMetadata(KeyTime.Uniform)); @@ -34,21 +34,27 @@ public abstract class GeometryKeyFrame : Freezable, IKeyFrame object IKeyFrame.Value { get => Value; - set => Value = (double[])value; + set => Value = (Geometry)value; } public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( - "Value", typeof(Geometry), typeof(GeometryKeyFrame), new PropertyMetadata(default(double[]))); + "Value", typeof(Geometry), typeof(GeometryKeyFrame), new PropertyMetadata(default(Geometry), OnValueChanged)); - public double[] Value + private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - get => (double[]) GetValue(ValueProperty); + var obj = (GeometryKeyFrame) d; + var v = (Geometry) e.NewValue; + AnimationHelper.DecomposeGeometryStr(v.ToString(), out var arr); + obj.Numbers = arr; + } + + public Geometry Value + { + get => (Geometry) GetValue(ValueProperty); set => SetValue(ValueProperty, value); } - protected string[] Strings { get; set; } - - public Geometry InterpolateValue(Geometry baseValue, double keyFrameProgress) + public double[] InterpolateValue(double[] baseValue, double keyFrameProgress) { if (keyFrameProgress < 0.0 || keyFrameProgress > 1.0) { @@ -58,5 +64,5 @@ public abstract class GeometryKeyFrame : Freezable, IKeyFrame return InterpolateValueCore(baseValue, keyFrameProgress); } - protected abstract Geometry InterpolateValueCore(Geometry baseValue, double keyFrameProgress); + protected abstract double[] InterpolateValueCore(double[] baseValue, double keyFrameProgress); } \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrameCollection.cs b/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrameCollection.cs index a65a27e4..dcf1b198 100644 --- a/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrameCollection.cs +++ b/src/Shared/HandyControl_Shared/Media/Animation/GeometryKeyFrameCollection.cs @@ -34,7 +34,7 @@ namespace HandyControl.Media.Animation } return s_emptyCollection; - } + } } public new GeometryKeyFrameCollection Clone() => (GeometryKeyFrameCollection)base.Clone(); diff --git a/src/Shared/HandyControl_Shared/Media/Animation/LinearGeometryKeyFrame.cs b/src/Shared/HandyControl_Shared/Media/Animation/LinearGeometryKeyFrame.cs index a6ad8cb4..71e9a372 100644 --- a/src/Shared/HandyControl_Shared/Media/Animation/LinearGeometryKeyFrame.cs +++ b/src/Shared/HandyControl_Shared/Media/Animation/LinearGeometryKeyFrame.cs @@ -8,8 +8,6 @@ namespace HandyControl.Media.Animation { public class LinearGeometryKeyFrame : GeometryKeyFrame { - private double[] _baseValues; - public LinearGeometryKeyFrame() { @@ -27,13 +25,8 @@ namespace HandyControl.Media.Animation protected override Freezable CreateInstanceCore() => new LinearGeometryKeyFrame(); - protected override Geometry InterpolateValueCore(Geometry baseValue, double keyFrameProgress) + protected override double[] InterpolateValueCore(double[] baseValue, double keyFrameProgress) { - if (_baseValues == null) - { - AnimationHelper.DecomposeGeometryStr(baseValue.ToString(), out _baseValues); - } - if (MathHelper.IsVerySmall(keyFrameProgress)) { return baseValue; @@ -41,10 +34,10 @@ namespace HandyControl.Media.Animation if (MathHelper.AreClose(keyFrameProgress, 1)) { - return AnimationHelper.ComposeGeometry(Strings, Value); + return Numbers; } - return AnimationHelper.InterpolateGeometry(_baseValues, Value, keyFrameProgress, Strings); + return AnimationHelper.InterpolateGeometryValue(baseValue, Numbers, keyFrameProgress); } } } diff --git a/src/Shared/HandyControl_Shared/Media/Animation/ResolvedKeyFrameEntry.cs b/src/Shared/HandyControl_Shared/Media/Animation/ResolvedKeyFrameEntry.cs new file mode 100644 index 00000000..7531cffc --- /dev/null +++ b/src/Shared/HandyControl_Shared/Media/Animation/ResolvedKeyFrameEntry.cs @@ -0,0 +1,38 @@ +using System; + +namespace HandyControl.Media.Animation +{ + internal struct ResolvedKeyFrameEntry : IComparable + { + internal int _originalKeyFrameIndex; + + internal TimeSpan _resolvedKeyTime; + + public int CompareTo(object other) + { + var otherEntry = (ResolvedKeyFrameEntry)other; + + if (otherEntry._resolvedKeyTime > _resolvedKeyTime) + { + return -1; + } + + if (otherEntry._resolvedKeyTime < _resolvedKeyTime) + { + return 1; + } + + if (otherEntry._originalKeyFrameIndex > _originalKeyFrameIndex) + { + return -1; + } + + if (otherEntry._originalKeyFrameIndex < _originalKeyFrameIndex) + { + return 1; + } + + return 0; + } + } +} diff --git a/src/Shared/HandyControl_Shared/Media/Animation/SplineGeometryKeyFrame.cs b/src/Shared/HandyControl_Shared/Media/Animation/SplineGeometryKeyFrame.cs index d501c2db..b04ad928 100644 --- a/src/Shared/HandyControl_Shared/Media/Animation/SplineGeometryKeyFrame.cs +++ b/src/Shared/HandyControl_Shared/Media/Animation/SplineGeometryKeyFrame.cs @@ -9,8 +9,6 @@ namespace HandyControl.Media.Animation { public class SplineGeometryKeyFrame : GeometryKeyFrame { - private double[] _baseValues; - public SplineGeometryKeyFrame() { @@ -33,13 +31,8 @@ namespace HandyControl.Media.Animation protected override Freezable CreateInstanceCore() => new SplineGeometryKeyFrame(); - protected override Geometry InterpolateValueCore(Geometry baseValue, double keyFrameProgress) + protected override double[] InterpolateValueCore(double[] baseValue, double keyFrameProgress) { - if (_baseValues == null) - { - AnimationHelper.DecomposeGeometryStr(baseValue.ToString(), out _baseValues); - } - if (MathHelper.IsVerySmall(keyFrameProgress)) { return baseValue; @@ -47,11 +40,11 @@ namespace HandyControl.Media.Animation if (MathHelper.AreClose(keyFrameProgress, 1)) { - return AnimationHelper.ComposeGeometry(Strings, Value); + return Numbers; } var splineProgress = KeySpline.GetSplineProgress(keyFrameProgress); - return AnimationHelper.InterpolateGeometry(_baseValues, Value, splineProgress, Strings); + return AnimationHelper.InterpolateGeometryValue(baseValue, Numbers, splineProgress); } public static readonly DependencyProperty KeySplineProperty = DependencyProperty.Register( diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs index 1a46a868..851a8dc1 100644 --- a/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs +++ b/src/Shared/HandyControl_Shared/Tools/Helper/AnimationHelper.cs @@ -78,5 +78,17 @@ namespace HandyControl.Tools return ComposeGeometry(strings, accumulated); } + + internal static double[] InterpolateGeometryValue(double[] from, double[] to, double progress) + { + var accumulated = new double[to.Length]; + for (var i = 0; i < to.Length; i++) + { + var fromValue = from[i]; + accumulated[i] = fromValue + (to[i] - fromValue) * progress; + } + + return accumulated; + } } } \ No newline at end of file From 09f116d4a6bd746581465eb4eee2b494291d328b Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Sat, 28 Sep 2019 00:32:24 +0800 Subject: [PATCH 10/10] add GeometryAnimation Demo --- README.md | 8 +++-- .../Data/MessageToken.cs | 2 ++ .../Data/MessageToken.tt | 3 +- .../HandyControlDemo_Shared.projitems | 7 ++++ .../Properties/Langs/Lang.Designer.cs | 9 +++++ .../Properties/Langs/Lang.en.resx | 3 ++ .../Properties/Langs/Lang.fa.resx | 3 ++ .../Properties/Langs/Lang.fr.resx | 3 ++ .../Properties/Langs/Lang.resx | 3 ++ .../UserControl/Main/LeftMainContent.xaml | 5 +++ .../Tools/GeometryAnimationDemoCtl.xaml | 36 +++++++++++++++++++ .../Tools/GeometryAnimationDemoCtl.xaml.cs | 10 ++++++ 12 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 src/Shared/HandyControlDemo_Shared/UserControl/Tools/GeometryAnimationDemoCtl.xaml create mode 100644 src/Shared/HandyControlDemo_Shared/UserControl/Tools/GeometryAnimationDemoCtl.xaml.cs diff --git a/README.md b/README.md index faeaf3ec..7e4a9e73 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,10 @@ Step 4:enjoy coding ## Latest examples +### GeometryAnimation + +![GeometryAnimation](https://raw.githubusercontent.com/HandyOrg/HandyOrgResource/master/HandyControl/Resources/GeometryAnimation.gif) + ### SplitButton ![SplitButton](https://raw.githubusercontent.com/HandyOrg/HandyOrgResource/master/HandyControl/Resources/SplitButton.png) @@ -85,12 +89,12 @@ Step 4:enjoy coding ![ImageBlock](https://raw.githubusercontent.com/HandyOrg/HandyOrgResource/master/HandyControl/Resources/ImageBlock.png) +## History publication + ### Magnifier ![Magnifier](https://raw.githubusercontent.com/HandyOrg/HandyOrgResource/master/HandyControl/Resources/Magnifier.png) -## History publication - ### Card ![Card](https://raw.githubusercontent.com/HandyOrg/HandyOrgResource/master/HandyControl/Resources/Card.png) diff --git a/src/Shared/HandyControlDemo_Shared/Data/MessageToken.cs b/src/Shared/HandyControlDemo_Shared/Data/MessageToken.cs index ecf3653f..717a71ac 100644 --- a/src/Shared/HandyControlDemo_Shared/Data/MessageToken.cs +++ b/src/Shared/HandyControlDemo_Shared/Data/MessageToken.cs @@ -124,6 +124,8 @@ public static readonly string HatchBrushGeneratorDemoCtl = nameof(HatchBrushGeneratorDemoCtl); + public static readonly string GeometryAnimationDemoCtl = nameof(GeometryAnimationDemoCtl); + /*-------------------- Styles --------------------*/ public static readonly string ButtonDemoCtl = nameof(ButtonDemoCtl); diff --git a/src/Shared/HandyControlDemo_Shared/Data/MessageToken.tt b/src/Shared/HandyControlDemo_Shared/Data/MessageToken.tt index 53ce827c..91d84fbb 100644 --- a/src/Shared/HandyControlDemo_Shared/Data/MessageToken.tt +++ b/src/Shared/HandyControlDemo_Shared/Data/MessageToken.tt @@ -96,7 +96,8 @@ var styleList = new List }; var toolList = new List { - "HatchBrushGeneratorDemoCtl" + "HatchBrushGeneratorDemoCtl", + "GeometryAnimationDemoCtl" }; var windowList = new List { diff --git a/src/Shared/HandyControlDemo_Shared/HandyControlDemo_Shared.projitems b/src/Shared/HandyControlDemo_Shared/HandyControlDemo_Shared.projitems index b52b7007..b13bd738 100644 --- a/src/Shared/HandyControlDemo_Shared/HandyControlDemo_Shared.projitems +++ b/src/Shared/HandyControlDemo_Shared/HandyControlDemo_Shared.projitems @@ -328,6 +328,9 @@ TreeViewDemoCtl.xaml + + GeometryAnimationDemoCtl.xaml + HatchBrushGeneratorDemoCtl.xaml @@ -821,6 +824,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.Designer.cs b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.Designer.cs index b06c9166..b26bcdf7 100644 --- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.Designer.cs +++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.Designer.cs @@ -843,6 +843,15 @@ namespace HandyControlDemo.Properties.Langs { } } + /// + /// 查找类似 变形动画 的本地化字符串。 + /// + public static string Morphing_Animation { + get { + return ResourceManager.GetString("Morphing Animation", resourceCulture); + } + } + /// /// 查找类似 名称 的本地化字符串。 /// diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.en.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.en.resx index 29bce738..efa53531 100644 --- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.en.resx +++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.en.resx @@ -672,4 +672,7 @@ HatchBrushGenerator + + Morphing Animation + \ No newline at end of file diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fa.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fa.resx index 611a9438..3ee98c1b 100644 --- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fa.resx +++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fa.resx @@ -672,4 +672,7 @@ HatchBrushGenerator + + Morphing Animation + \ No newline at end of file diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fr.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fr.resx index b0d16fb4..6c7214be 100644 --- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fr.resx +++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fr.resx @@ -672,4 +672,7 @@ HatchBrushGenerator + + Morphing Animation + \ No newline at end of file diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.resx index 3131e9b0..3950a730 100644 --- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.resx +++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.resx @@ -672,4 +672,7 @@ 阴影画笔生成器 + + 变形动画 + \ No newline at end of file diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml index adf8987d..a3bd5714 100644 --- a/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml +++ b/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml @@ -503,6 +503,11 @@ + + + + + diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Tools/GeometryAnimationDemoCtl.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Tools/GeometryAnimationDemoCtl.xaml new file mode 100644 index 00000000..ede0f570 --- /dev/null +++ b/src/Shared/HandyControlDemo_Shared/UserControl/Tools/GeometryAnimationDemoCtl.xaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Tools/GeometryAnimationDemoCtl.xaml.cs b/src/Shared/HandyControlDemo_Shared/UserControl/Tools/GeometryAnimationDemoCtl.xaml.cs new file mode 100644 index 00000000..959bc541 --- /dev/null +++ b/src/Shared/HandyControlDemo_Shared/UserControl/Tools/GeometryAnimationDemoCtl.xaml.cs @@ -0,0 +1,10 @@ +namespace HandyControlDemo.UserControl +{ + public partial class GeometryAnimationDemoCtl + { + public GeometryAnimationDemoCtl() + { + InitializeComponent(); + } + } +}