add GeometryAnimationUsingKeyFrames
This commit is contained in:
1 parent
f549793e13
commit
0e74e5b34e
12 files changed
+595
-73
No files matched your search
@@ -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;
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
+495
-11
@@ -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<KeyTimeBlock>();
|
||||
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<double>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -34,7 +34,7 @@ namespace HandyControl.Media.Animation
|
||||
}
|
||||
|
||||
return s_emptyCollection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new GeometryKeyFrameCollection Clone() => (GeometryKeyFrameCollection)base.Clone();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
Reference in new issue
Block a user