diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Animation/Animation.cs index 1ce9f5db06..ca1d97290e 100644 --- a/src/Avalonia.Animation/Animation.cs +++ b/src/Avalonia.Animation/Animation.cs @@ -254,7 +254,7 @@ namespace Avalonia.Animation cue = new Cue(keyframe.KeyTime.TotalSeconds / Duration.TotalSeconds); } - var newKF = new AnimatorKeyFrame(handler, cue); + var newKF = new AnimatorKeyFrame(handler, cue, keyframe.KeySpline); subscriptions.Add(newKF.BindSetter(setter, control)); diff --git a/src/Avalonia.Animation/AnimatorKeyFrame.cs b/src/Avalonia.Animation/AnimatorKeyFrame.cs index 36d15e518e..f6a0c12be4 100644 --- a/src/Avalonia.Animation/AnimatorKeyFrame.cs +++ b/src/Avalonia.Animation/AnimatorKeyFrame.cs @@ -24,11 +24,20 @@ namespace Avalonia.Animation { AnimatorType = animatorType; Cue = cue; + KeySpline = null; + } + + public AnimatorKeyFrame(Type animatorType, Cue cue, KeySpline keySpline) + { + AnimatorType = animatorType; + Cue = cue; + KeySpline = keySpline; } internal bool isNeutral; public Type AnimatorType { get; } public Cue Cue { get; } + public KeySpline KeySpline { get; } public AvaloniaProperty Property { get; private set; } private object _value; diff --git a/src/Avalonia.Animation/Animators/Animator`1.cs b/src/Avalonia.Animation/Animators/Animator`1.cs index aa5e6aaf14..121ffda564 100644 --- a/src/Avalonia.Animation/Animators/Animator`1.cs +++ b/src/Avalonia.Animation/Animators/Animator`1.cs @@ -89,6 +89,9 @@ namespace Avalonia.Animation.Animators else newValue = (T)lastKeyframe.Value; + if (lastKeyframe.KeySpline != null) // TODO: do we use firstKeyFrame or lastKeyframe?! + progress = lastKeyframe.KeySpline.GetSplineProgress(progress); + return Interpolate(progress, oldValue, newValue); } diff --git a/src/Avalonia.Animation/KeyFrame.cs b/src/Avalonia.Animation/KeyFrame.cs index ec59586584..c2cc1aa051 100644 --- a/src/Avalonia.Animation/KeyFrame.cs +++ b/src/Avalonia.Animation/KeyFrame.cs @@ -19,6 +19,7 @@ namespace Avalonia.Animation { private TimeSpan _ktimeSpan; private Cue _kCue; + private KeySpline _kKeySpline; public KeyFrame() { @@ -74,6 +75,25 @@ namespace Avalonia.Animation } } + /// + /// Gets or sets the KeySpline of this . + /// + /// The key spline. + public KeySpline KeySpline + { + get + { + return _kKeySpline; + } + set + { + _kKeySpline = value; + if (value != null && !value.IsValid()) + { + throw new ArgumentException($"{nameof(KeySpline)} must have X coordinates >= 0.0 and <= 1.0."); + } + } + } } diff --git a/src/Avalonia.Animation/KeySpline.cs b/src/Avalonia.Animation/KeySpline.cs index 34e8d89aae..dc6fb61744 100644 --- a/src/Avalonia.Animation/KeySpline.cs +++ b/src/Avalonia.Animation/KeySpline.cs @@ -66,7 +66,17 @@ namespace Avalonia.Animation public double ControlPointX1 { get => _controlPointX1; - set => _controlPointX1 = value; + set + { + if (IsValidXValue(value)) + { + _controlPointX1 = value; + } + else + { + throw new ArgumentException("Invalid KeySpline X1 value. Must be >= 0.0 and <= 1.0."); + } + } } public double ControlPointY1 @@ -78,7 +88,17 @@ namespace Avalonia.Animation public double ControlPointX2 { get => _controlPointX2; - set => _controlPointX2 = value; + set + { + if (IsValidXValue(value)) + { + _controlPointX2 = value; + } + else + { + throw new ArgumentException("Invalid KeySpline X2 value. Must be >= 0.0 and <= 1.0."); + } + } } public double ControlPointY2 @@ -94,8 +114,6 @@ namespace Avalonia.Animation /// the spline progress public double GetSplineProgress(double linearProgress) { - //ReadPreamble(); - if (_isDirty) { Build(); @@ -113,6 +131,16 @@ namespace Avalonia.Animation } } + public bool IsValid() + { + return IsValidXValue(_controlPointX1) && IsValidXValue(_controlPointX2); + } + + private bool IsValidXValue(double value) + { + return value >= 0.0 && value <= 1.0; + } + /// /// Compute cached coefficients. ///