committed by
GitHub
67 changed files with 1523 additions and 572 deletions
@ -0,0 +1,21 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="bool"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class BoolAnimator : Animator<bool> |
||||
|
{ |
||||
|
/// <inheritdocs/>
|
||||
|
public override bool Interpolate(double progress, bool oldValue, bool newValue) |
||||
|
{ |
||||
|
if(progress >= 1d) |
||||
|
return newValue; |
||||
|
if(progress >= 0) |
||||
|
return oldValue; |
||||
|
return oldValue; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="byte"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class ByteAnimator : Animator<byte> |
||||
|
{ |
||||
|
const double maxVal = (double)byte.MaxValue; |
||||
|
|
||||
|
/// <inheritdocs/>
|
||||
|
public override byte Interpolate(double progress, byte oldValue, byte newValue) |
||||
|
{ |
||||
|
var normOV = oldValue / maxVal; |
||||
|
var normNV = newValue / maxVal; |
||||
|
var deltaV = normNV - normOV; |
||||
|
return (byte)Math.Round(maxVal * ((deltaV * progress) + normOV)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="decimal"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class DecimalAnimator : Animator<decimal> |
||||
|
{ |
||||
|
/// <inheritdocs/>
|
||||
|
public override decimal Interpolate(double progress, decimal oldValue, decimal newValue) |
||||
|
{ |
||||
|
return ((newValue - oldValue) * (decimal)progress) + oldValue; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="double"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class DoubleAnimator : Animator<double> |
||||
|
{ |
||||
|
/// <inheritdocs/>
|
||||
|
public override double Interpolate(double progress, double oldValue, double newValue) |
||||
|
{ |
||||
|
return ((newValue - oldValue) * progress) + oldValue; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="float"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class FloatAnimator : Animator<float> |
||||
|
{ |
||||
|
/// <inheritdocs/>
|
||||
|
public override float Interpolate(double progress, float oldValue, float newValue) |
||||
|
{ |
||||
|
return (float)(((newValue - oldValue) * progress) + oldValue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="Int16"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class Int16Animator : Animator<Int16> |
||||
|
{ |
||||
|
const double maxVal = (double)Int16.MaxValue; |
||||
|
|
||||
|
/// <inheritdocs/>
|
||||
|
public override Int16 Interpolate(double progress, Int16 oldValue, Int16 newValue) |
||||
|
{ |
||||
|
var normOV = oldValue / maxVal; |
||||
|
var normNV = newValue / maxVal; |
||||
|
var deltaV = normNV - normOV; |
||||
|
return (Int16)Math.Round(maxVal * ((deltaV * progress) + normOV)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="Int32"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class Int32Animator : Animator<Int32> |
||||
|
{ |
||||
|
const double maxVal = (double)Int32.MaxValue; |
||||
|
|
||||
|
/// <inheritdocs/>
|
||||
|
public override Int32 Interpolate(double progress, Int32 oldValue, Int32 newValue) |
||||
|
{ |
||||
|
var normOV = oldValue / maxVal; |
||||
|
var normNV = newValue / maxVal; |
||||
|
var deltaV = normNV - normOV; |
||||
|
return (Int32)Math.Round(maxVal * ((deltaV * progress) + normOV)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="Int64"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class Int64Animator : Animator<Int64> |
||||
|
{ |
||||
|
const double maxVal = (double)Int64.MaxValue; |
||||
|
|
||||
|
/// <inheritdocs/>
|
||||
|
public override Int64 Interpolate(double progress, Int64 oldValue, Int64 newValue) |
||||
|
{ |
||||
|
var normOV = oldValue / maxVal; |
||||
|
var normNV = newValue / maxVal; |
||||
|
var deltaV = normNV - normOV; |
||||
|
return (Int64)Math.Round(maxVal * ((deltaV * progress) + normOV)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="UInt16"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class UInt16Animator : Animator<UInt16> |
||||
|
{ |
||||
|
const double maxVal = (double)UInt16.MaxValue; |
||||
|
|
||||
|
/// <inheritdocs/>
|
||||
|
public override UInt16 Interpolate(double progress, UInt16 oldValue, UInt16 newValue) |
||||
|
{ |
||||
|
var normOV = oldValue / maxVal; |
||||
|
var normNV = newValue / maxVal; |
||||
|
var deltaV = normNV - normOV; |
||||
|
return (UInt16)Math.Round(maxVal * ((deltaV * progress) + normOV)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="UInt32"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class UInt32Animator : Animator<UInt32> |
||||
|
{ |
||||
|
const double maxVal = (double)UInt32.MaxValue; |
||||
|
|
||||
|
/// <inheritdocs/>
|
||||
|
public override UInt32 Interpolate(double progress, UInt32 oldValue, UInt32 newValue) |
||||
|
{ |
||||
|
var normOV = oldValue / maxVal; |
||||
|
var normNV = newValue / maxVal; |
||||
|
var deltaV = normNV - normOV; |
||||
|
return (UInt32)Math.Round(maxVal * ((deltaV * progress) + normOV)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="UInt64"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class UInt64Animator : Animator<UInt64> |
||||
|
{ |
||||
|
const double maxVal = (double)UInt64.MaxValue; |
||||
|
|
||||
|
/// <inheritdocs/>
|
||||
|
public override UInt64 Interpolate(double progress, UInt64 oldValue, UInt64 newValue) |
||||
|
{ |
||||
|
var normOV = oldValue / maxVal; |
||||
|
var normNV = newValue / maxVal; |
||||
|
var deltaV = normNV - normOV; |
||||
|
return (UInt64)Math.Round(maxVal * ((deltaV * progress) + normOV)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,35 +0,0 @@ |
|||||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
||||
|
|
||||
namespace Avalonia.Animation |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Animator that handles <see cref="double"/> properties.
|
|
||||
/// </summary>
|
|
||||
public class DoubleAnimator : Animator<double> |
|
||||
{ |
|
||||
|
|
||||
/// <inheritdocs/>
|
|
||||
protected override double DoInterpolation(double t, double neutralValue) |
|
||||
{ |
|
||||
var pair = GetKFPairAndIntraKFTime(t); |
|
||||
double y0, y1; |
|
||||
|
|
||||
var firstKF = pair.KFPair.FirstKeyFrame; |
|
||||
var secondKF = pair.KFPair.SecondKeyFrame; |
|
||||
|
|
||||
if (firstKF.isNeutral) |
|
||||
y0 = neutralValue; |
|
||||
else |
|
||||
y0 = firstKF.TargetValue; |
|
||||
|
|
||||
if (secondKF.isNeutral) |
|
||||
y1 = neutralValue; |
|
||||
else |
|
||||
y1 = secondKF.TargetValue; |
|
||||
|
|
||||
// Do linear parametric interpolation
|
|
||||
return y0 + (pair.IntraKFTime) * (y1 - y0); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,176 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Globalization; |
||||
|
|
||||
|
namespace Avalonia.Animation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines the valid modes for a <see cref="IterationCount"/>.
|
||||
|
/// </summary>
|
||||
|
public enum IterationType |
||||
|
{ |
||||
|
Many, |
||||
|
Infinite |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Determines the number of iterations of an animation.
|
||||
|
/// Also defines its repeat behavior.
|
||||
|
/// </summary>
|
||||
|
[TypeConverter(typeof(IterationCountTypeConverter))] |
||||
|
public struct IterationCount : IEquatable<IterationCount> |
||||
|
{ |
||||
|
private readonly IterationType _type; |
||||
|
private readonly ulong _value; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="IterationCount"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The number of iterations of an animation.</param>
|
||||
|
public IterationCount(ulong value) |
||||
|
: this(value, IterationType.Many) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="IterationCount"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The size of the IterationCount.</param>
|
||||
|
/// <param name="type">The unit of the IterationCount.</param>
|
||||
|
public IterationCount(ulong value, IterationType type) |
||||
|
{ |
||||
|
if (type > IterationType.Infinite) |
||||
|
{ |
||||
|
throw new ArgumentException("Invalid value", "type"); |
||||
|
} |
||||
|
|
||||
|
_type = type; |
||||
|
_value = value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets an instance of <see cref="IterationCount"/> that indicates that an animation
|
||||
|
/// should repeat forever.
|
||||
|
/// </summary>
|
||||
|
public static IterationCount Infinite => new IterationCount(0, IterationType.Infinite); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the unit of the <see cref="IterationCount"/>.
|
||||
|
/// </summary>
|
||||
|
public IterationType RepeatType => _type; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value that indicates whether the <see cref="IterationCount"/> is set to loop.
|
||||
|
/// </summary>
|
||||
|
public bool IsInfinite => _type == IterationType.Infinite; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the number of repeat iterations.
|
||||
|
/// </summary>
|
||||
|
public ulong Value => _value; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two IterationCount structures for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="a">The first IterationCount.</param>
|
||||
|
/// <param name="b">The second IterationCount.</param>
|
||||
|
/// <returns>True if the structures are equal, otherwise false.</returns>
|
||||
|
public static bool operator ==(IterationCount a, IterationCount b) |
||||
|
{ |
||||
|
return (a.IsInfinite && b.IsInfinite) |
||||
|
|| (a._value == b._value && a._type == b._type); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two IterationCount structures for inequality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="rc1">The first IterationCount.</param>
|
||||
|
/// <param name="rc2">The first IterationCount.</param>
|
||||
|
/// <returns>True if the structures are unequal, otherwise false.</returns>
|
||||
|
public static bool operator !=(IterationCount rc1, IterationCount rc2) |
||||
|
{ |
||||
|
return !(rc1 == rc2); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Determines whether the <see cref="IterationCount"/> is equal to the specified object.
|
||||
|
/// </summary>
|
||||
|
/// <param name="o">The object with which to test equality.</param>
|
||||
|
/// <returns>True if the objects are equal, otherwise false.</returns>
|
||||
|
public override bool Equals(object o) |
||||
|
{ |
||||
|
if (o == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (!(o is IterationCount)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return this == (IterationCount)o; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two IterationCount structures for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="IterationCount">The structure with which to test equality.</param>
|
||||
|
/// <returns>True if the structures are equal, otherwise false.</returns>
|
||||
|
public bool Equals(IterationCount IterationCount) |
||||
|
{ |
||||
|
return this == IterationCount; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a hash code for the IterationCount.
|
||||
|
/// </summary>
|
||||
|
/// <returns>The hash code.</returns>
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return _value.GetHashCode() ^ _type.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a string representation of the <see cref="IterationCount"/>.
|
||||
|
/// </summary>
|
||||
|
/// <returns>The string representation.</returns>
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
if (IsInfinite) |
||||
|
{ |
||||
|
return "Infinite"; |
||||
|
} |
||||
|
|
||||
|
string s = _value.ToString(); |
||||
|
return s; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Parses a string to return a <see cref="IterationCount"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="s">The string.</param>
|
||||
|
/// <returns>The <see cref="IterationCount"/>.</returns>
|
||||
|
public static IterationCount Parse(string s) |
||||
|
{ |
||||
|
s = s.ToUpperInvariant().Trim(); |
||||
|
|
||||
|
if (s.EndsWith("INFINITE")) |
||||
|
{ |
||||
|
return Infinite; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (s.StartsWith("-")) |
||||
|
throw new InvalidCastException("IterationCount can't be a negative number."); |
||||
|
|
||||
|
var value = ulong.Parse(s, CultureInfo.InvariantCulture); |
||||
|
|
||||
|
return new IterationCount(value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,33 +0,0 @@ |
|||||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
||||
|
|
||||
namespace Avalonia.Animation |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a pair of keyframe, usually the
|
|
||||
/// Start and End keyframes of a <see cref="Animator{T}"/> object.
|
|
||||
/// </summary>
|
|
||||
public struct KeyFramePair<T> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes this <see cref="KeyFramePair{T}"/>
|
|
||||
/// </summary>
|
|
||||
/// <param name="FirstKeyFrame"></param>
|
|
||||
/// <param name="LastKeyFrame"></param>
|
|
||||
public KeyFramePair((T TargetValue, bool isNeutral) FirstKeyFrame, (T TargetValue, bool isNeutral) LastKeyFrame) : this() |
|
||||
{ |
|
||||
this.FirstKeyFrame = FirstKeyFrame; |
|
||||
this.SecondKeyFrame = LastKeyFrame; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// First <see cref="KeyFrame"/> object.
|
|
||||
/// </summary>
|
|
||||
public (T TargetValue, bool isNeutral) FirstKeyFrame { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Second <see cref="KeyFrame"/> object.
|
|
||||
/// </summary>
|
|
||||
public (T TargetValue, bool isNeutral) SecondKeyFrame { get; } |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,33 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Collections; |
||||
|
|
||||
|
namespace Avalonia.Animation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A collection of <see cref="KeyFrame"/>s.
|
||||
|
/// </summary>
|
||||
|
public class KeyFrames : AvaloniaList<KeyFrame> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="KeyFrames"/> class.
|
||||
|
/// </summary>
|
||||
|
public KeyFrames() |
||||
|
{ |
||||
|
ResetBehavior = ResetBehavior.Remove; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="KeyFrames"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="items">The initial items in the collection.</param>
|
||||
|
public KeyFrames(IEnumerable<KeyFrame> items) |
||||
|
: base(items) |
||||
|
{ |
||||
|
ResetBehavior = ResetBehavior.Remove; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,199 +0,0 @@ |
|||||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
||||
|
|
||||
using System; |
|
||||
using System.ComponentModel; |
|
||||
using System.Globalization; |
|
||||
|
|
||||
namespace Avalonia.Animation |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Defines the valid modes for a <see cref="RepeatCount"/>.
|
|
||||
/// </summary>
|
|
||||
public enum RepeatType |
|
||||
{ |
|
||||
None, |
|
||||
Repeat, |
|
||||
Loop |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Determines the number of iterations of an animation.
|
|
||||
/// Also defines its repeat behavior.
|
|
||||
/// </summary>
|
|
||||
[TypeConverter(typeof(RepeatCountTypeConverter))] |
|
||||
public struct RepeatCount : IEquatable<RepeatCount> |
|
||||
{ |
|
||||
private readonly RepeatType _type; |
|
||||
private readonly ulong _value; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="RepeatCount"/> struct.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The number of iterations of an animation.</param>
|
|
||||
public RepeatCount(ulong value) |
|
||||
: this(value, RepeatType.Repeat) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="RepeatCount"/> struct.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The size of the RepeatCount.</param>
|
|
||||
/// <param name="type">The unit of the RepeatCount.</param>
|
|
||||
public RepeatCount(ulong value, RepeatType type) |
|
||||
{ |
|
||||
if (type < RepeatType.None || type > RepeatType.Loop) |
|
||||
{ |
|
||||
throw new ArgumentException("Invalid value", "type"); |
|
||||
} |
|
||||
|
|
||||
_type = type; |
|
||||
_value = value; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets an instance of <see cref="RepeatCount"/> that indicates that an animation
|
|
||||
/// should repeat forever.
|
|
||||
/// </summary>
|
|
||||
public static RepeatCount Loop => new RepeatCount(0, RepeatType.Loop); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets an instance of <see cref="RepeatCount"/> that indicates that an animation
|
|
||||
/// should not repeat.
|
|
||||
/// </summary>
|
|
||||
public static RepeatCount None => new RepeatCount(0, RepeatType.None); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the unit of the <see cref="RepeatCount"/>.
|
|
||||
/// </summary>
|
|
||||
public RepeatType RepeatType => _type; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a value that indicates whether the <see cref="RepeatCount"/> is set to loop.
|
|
||||
/// </summary>
|
|
||||
public bool IsLoop => _type == RepeatType.Loop; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a value that indicates whether the <see cref="RepeatCount"/> is set to not repeat.
|
|
||||
/// </summary>
|
|
||||
public bool IsNone => _type == RepeatType.None; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the number of repeat iterations.
|
|
||||
/// </summary>
|
|
||||
public ulong Value => _value; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Compares two RepeatCount structures for equality.
|
|
||||
/// </summary>
|
|
||||
/// <param name="a">The first RepeatCount.</param>
|
|
||||
/// <param name="b">The second RepeatCount.</param>
|
|
||||
/// <returns>True if the structures are equal, otherwise false.</returns>
|
|
||||
public static bool operator ==(RepeatCount a, RepeatCount b) |
|
||||
{ |
|
||||
return (a.IsNone && b.IsNone) && (a.IsLoop && b.IsLoop) |
|
||||
|| (a._value == b._value && a._type == b._type); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Compares two RepeatCount structures for inequality.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rc1">The first RepeatCount.</param>
|
|
||||
/// <param name="rc2">The first RepeatCount.</param>
|
|
||||
/// <returns>True if the structures are unequal, otherwise false.</returns>
|
|
||||
public static bool operator !=(RepeatCount rc1, RepeatCount rc2) |
|
||||
{ |
|
||||
return !(rc1 == rc2); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Determines whether the <see cref="RepeatCount"/> is equal to the specified object.
|
|
||||
/// </summary>
|
|
||||
/// <param name="o">The object with which to test equality.</param>
|
|
||||
/// <returns>True if the objects are equal, otherwise false.</returns>
|
|
||||
public override bool Equals(object o) |
|
||||
{ |
|
||||
if (o == null) |
|
||||
{ |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
if (!(o is RepeatCount)) |
|
||||
{ |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
return this == (RepeatCount)o; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Compares two RepeatCount structures for equality.
|
|
||||
/// </summary>
|
|
||||
/// <param name="RepeatCount">The structure with which to test equality.</param>
|
|
||||
/// <returns>True if the structures are equal, otherwise false.</returns>
|
|
||||
public bool Equals(RepeatCount RepeatCount) |
|
||||
{ |
|
||||
return this == RepeatCount; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a hash code for the RepeatCount.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The hash code.</returns>
|
|
||||
public override int GetHashCode() |
|
||||
{ |
|
||||
return _value.GetHashCode() ^ _type.GetHashCode(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a string representation of the <see cref="RepeatCount"/>.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The string representation.</returns>
|
|
||||
public override string ToString() |
|
||||
{ |
|
||||
if (IsLoop) |
|
||||
{ |
|
||||
return "Auto"; |
|
||||
} |
|
||||
else if (IsNone) |
|
||||
{ |
|
||||
return "None"; |
|
||||
} |
|
||||
|
|
||||
string s = _value.ToString(); |
|
||||
return s; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Parses a string to return a <see cref="RepeatCount"/>.
|
|
||||
/// </summary>
|
|
||||
/// <param name="s">The string.</param>
|
|
||||
/// <returns>The <see cref="RepeatCount"/>.</returns>
|
|
||||
public static RepeatCount Parse(string s) |
|
||||
{ |
|
||||
s = s.ToUpperInvariant().Trim(); |
|
||||
|
|
||||
if (s == "NONE") |
|
||||
{ |
|
||||
return None; |
|
||||
} |
|
||||
else if (s.EndsWith("LOOP")) |
|
||||
{ |
|
||||
return Loop; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
if(s.StartsWith("-")) |
|
||||
throw new InvalidCastException("RepeatCount can't be a negative number."); |
|
||||
|
|
||||
var value = ulong.Parse(s, CultureInfo.InvariantCulture); |
|
||||
|
|
||||
if (value == 1) |
|
||||
return None; |
|
||||
|
|
||||
return new RepeatCount(value); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,70 @@ |
|||||
|
// Original color interpolation code was written by Romain Guy and Francois Blavoet
|
||||
|
// and adopted from LottieSharp Project (https://github.com/ascora/LottieSharp).
|
||||
|
|
||||
|
using System; |
||||
|
using System.Reactive.Disposables; |
||||
|
using Avalonia.Logging; |
||||
|
using Avalonia.Media; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that interpolates <see cref="Color"/> through
|
||||
|
/// gamma sRGB color space for better visual result.
|
||||
|
/// </summary>
|
||||
|
public class ColorAnimator : Animator<Color> |
||||
|
{ |
||||
|
// Opto-electronic conversion function for the sRGB color space
|
||||
|
// Takes a gamma-encoded sRGB value and converts it to a linear sRGB value
|
||||
|
private static double OECF_sRGB(double linear) |
||||
|
{ |
||||
|
// IEC 61966-2-1:1999
|
||||
|
return linear <= 0.0031308d ? linear * 12.92d : (double)(Math.Pow(linear, 1.0d / 2.4d) * 1.055d - 0.055d); |
||||
|
} |
||||
|
|
||||
|
// Electro-optical conversion function for the sRGB color space
|
||||
|
// Takes a linear sRGB value and converts it to a gamma-encoded sRGB value
|
||||
|
private static double EOCF_sRGB(double srgb) |
||||
|
{ |
||||
|
// IEC 61966-2-1:1999
|
||||
|
return srgb <= 0.04045d ? srgb / 12.92d : (double)Math.Pow((srgb + 0.055d) / 1.055d, 2.4d); |
||||
|
} |
||||
|
|
||||
|
public override Color Interpolate(double progress, Color oldValue, Color newValue) |
||||
|
{ |
||||
|
// normalize sRGB values.
|
||||
|
var oldA = oldValue.A / 255d; |
||||
|
var oldR = oldValue.R / 255d; |
||||
|
var oldG = oldValue.G / 255d; |
||||
|
var oldB = oldValue.B / 255d; |
||||
|
|
||||
|
var newA = newValue.A / 255d; |
||||
|
var newR = newValue.R / 255d; |
||||
|
var newG = newValue.G / 255d; |
||||
|
var newB = newValue.B / 255d; |
||||
|
|
||||
|
// convert from sRGB to linear
|
||||
|
oldR = EOCF_sRGB(oldR); |
||||
|
oldG = EOCF_sRGB(oldG); |
||||
|
oldB = EOCF_sRGB(oldB); |
||||
|
|
||||
|
newR = EOCF_sRGB(newR); |
||||
|
newG = EOCF_sRGB(newG); |
||||
|
newB = EOCF_sRGB(newB); |
||||
|
|
||||
|
// compute the interpolated color in linear space
|
||||
|
var a = oldA + progress * (newA - oldA); |
||||
|
var r = oldR + progress * (newR - oldR); |
||||
|
var g = oldG + progress * (newG - oldG); |
||||
|
var b = oldB + progress * (newB - oldB); |
||||
|
|
||||
|
// convert back to sRGB in the [0..255] range
|
||||
|
a = a * 255d; |
||||
|
r = OECF_sRGB(r) * 255d; |
||||
|
g = OECF_sRGB(g) * 255d; |
||||
|
b = OECF_sRGB(b) * 255d; |
||||
|
|
||||
|
return new Color((byte)Math.Round(a), (byte)Math.Round(r), (byte)Math.Round(g), (byte)Math.Round(b)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Logging; |
||||
|
using Avalonia.Media; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="CornerRadius"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class CornerRadiusAnimator : Animator<CornerRadius> |
||||
|
{ |
||||
|
public override CornerRadius Interpolate(double progress, CornerRadius oldValue, CornerRadius newValue) |
||||
|
{ |
||||
|
var deltaTL = newValue.TopLeft - oldValue.TopLeft; |
||||
|
var deltaTR = newValue.TopRight - oldValue.TopRight; |
||||
|
var deltaBR = newValue.BottomRight - oldValue.BottomRight; |
||||
|
var deltaBL = newValue.BottomLeft - oldValue.BottomLeft; |
||||
|
|
||||
|
var nTL = progress * deltaTL + oldValue.TopLeft; |
||||
|
var nTR = progress * deltaTR + oldValue.TopRight; |
||||
|
var nBR = progress * deltaBR + oldValue.BottomRight; |
||||
|
var nBL = progress * deltaBL + oldValue.BottomLeft; |
||||
|
|
||||
|
return new CornerRadius(nTL, nTR, nBR, nBL); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Logging; |
||||
|
using Avalonia.Media; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="Point"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class PointAnimator : Animator<Point> |
||||
|
{ |
||||
|
public override Point Interpolate(double progress, Point oldValue, Point newValue) |
||||
|
{ |
||||
|
return ((newValue - oldValue) * progress) + oldValue; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Logging; |
||||
|
using Avalonia.Media; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="Rect"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class RectAnimator : Animator<Rect> |
||||
|
{ |
||||
|
public override Rect Interpolate(double progress, Rect oldValue, Rect newValue) |
||||
|
{ |
||||
|
var deltaPos = newValue.Position - oldValue.Position; |
||||
|
var deltaSize = newValue.Size - oldValue.Size; |
||||
|
|
||||
|
var newPos = (deltaPos * progress) + oldValue.Position; |
||||
|
var newSize = (deltaSize * progress) + oldValue.Size; |
||||
|
|
||||
|
return new Rect(newPos, newSize); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Logging; |
||||
|
using Avalonia.Media; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="Size"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class SizeAnimator : Animator<Size> |
||||
|
{ |
||||
|
public override Size Interpolate(double progress, Size oldValue, Size newValue) |
||||
|
{ |
||||
|
return ((newValue - oldValue) * progress) + oldValue; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
using System; |
||||
|
using System.Reactive.Disposables; |
||||
|
using Avalonia.Logging; |
||||
|
using Avalonia.Media; |
||||
|
using Avalonia.Media.Immutable; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="SolidColorBrush"/>.
|
||||
|
/// </summary>
|
||||
|
public class SolidColorBrushAnimator : Animator<SolidColorBrush> |
||||
|
{ |
||||
|
ColorAnimator _colorAnimator; |
||||
|
|
||||
|
void InitializeColorAnimator() |
||||
|
{ |
||||
|
_colorAnimator = new ColorAnimator(); |
||||
|
|
||||
|
foreach (AnimatorKeyFrame keyframe in this) |
||||
|
{ |
||||
|
_colorAnimator.Add(keyframe); |
||||
|
} |
||||
|
|
||||
|
_colorAnimator.Property = SolidColorBrush.ColorProperty; |
||||
|
} |
||||
|
|
||||
|
public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> match, Action onComplete) |
||||
|
{ |
||||
|
foreach (var keyframe in this) |
||||
|
{ |
||||
|
if (keyframe.Value as ISolidColorBrush == null) |
||||
|
return Disposable.Empty; |
||||
|
|
||||
|
// Preprocess keyframe values to Color if the xaml parser converts them to ISCB.
|
||||
|
if (keyframe.Value.GetType() == typeof(ImmutableSolidColorBrush)) |
||||
|
{ |
||||
|
keyframe.Value = ((ImmutableSolidColorBrush)keyframe.Value).Color; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Add SCB if the target prop is empty.
|
||||
|
if (control.GetValue(Property) == null) |
||||
|
control.SetValue(Property, new SolidColorBrush(Colors.Transparent)); |
||||
|
|
||||
|
var targetVal = control.GetValue(Property); |
||||
|
|
||||
|
// Continue if target prop is not empty & is a SolidColorBrush derivative.
|
||||
|
if (typeof(ISolidColorBrush).IsAssignableFrom(targetVal.GetType())) |
||||
|
{ |
||||
|
if (_colorAnimator == null) |
||||
|
InitializeColorAnimator(); |
||||
|
|
||||
|
SolidColorBrush finalTarget; |
||||
|
|
||||
|
// If it's ISCB, change it back to SCB.
|
||||
|
if (targetVal.GetType() == typeof(ImmutableSolidColorBrush)) |
||||
|
{ |
||||
|
var col = (ImmutableSolidColorBrush)targetVal; |
||||
|
targetVal = new SolidColorBrush(col.Color); |
||||
|
control.SetValue(Property, targetVal); |
||||
|
} |
||||
|
|
||||
|
finalTarget = targetVal as SolidColorBrush; |
||||
|
|
||||
|
return _colorAnimator.Apply(animation, finalTarget, clock ?? control.Clock, match, onComplete); |
||||
|
} |
||||
|
|
||||
|
return Disposable.Empty; |
||||
|
} |
||||
|
|
||||
|
public override SolidColorBrush Interpolate(double p, SolidColorBrush o, SolidColorBrush n) => null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Logging; |
||||
|
using Avalonia.Media; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="Thickness"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class ThicknessAnimator : Animator<Thickness> |
||||
|
{ |
||||
|
public override Thickness Interpolate(double progress, Thickness oldValue, Thickness newValue) |
||||
|
{ |
||||
|
return ((newValue - oldValue) * progress) + oldValue; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Logging; |
||||
|
using Avalonia.Media; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Animator that handles <see cref="Vector"/> properties.
|
||||
|
/// </summary>
|
||||
|
public class VectorAnimator : Animator<Vector> |
||||
|
{ |
||||
|
public override Vector Interpolate(double progress, Vector oldValue, Vector newValue) |
||||
|
{ |
||||
|
return ((newValue - oldValue) * progress) + oldValue; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Reactive.Linq; |
||||
|
|
||||
|
namespace Avalonia.Animation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="CornerRadius"/> type.
|
||||
|
/// </summary>
|
||||
|
public class CornerRadiusTransition : Transition<CornerRadius> |
||||
|
{ |
||||
|
/// <inheritdocs/>
|
||||
|
public override IObservable<CornerRadius> DoTransition(IObservable<double> progress, CornerRadius oldValue, CornerRadius newValue) |
||||
|
{ |
||||
|
return progress |
||||
|
.Select(p => |
||||
|
{ |
||||
|
var f = Easing.Ease(p); |
||||
|
|
||||
|
var deltaTL = newValue.TopLeft - oldValue.TopLeft; |
||||
|
var deltaTR = newValue.TopRight - oldValue.TopRight; |
||||
|
var deltaBR = newValue.BottomRight - oldValue.BottomRight; |
||||
|
var deltaBL = newValue.BottomLeft - oldValue.BottomLeft; |
||||
|
|
||||
|
var nTL = f * deltaTL + oldValue.TopLeft; |
||||
|
var nTR = f * deltaTR + oldValue.TopRight; |
||||
|
var nBR = f * deltaBR + oldValue.BottomRight; |
||||
|
var nBL = f * deltaBL + oldValue.BottomLeft; |
||||
|
|
||||
|
return new CornerRadius(nTL, nTR, nBR, nBL); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Reactive.Linq; |
||||
|
|
||||
|
namespace Avalonia.Animation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Size"/> type.
|
||||
|
/// </summary>
|
||||
|
public class SizeTransition : Transition<Size> |
||||
|
{ |
||||
|
/// <inheritdocs/>
|
||||
|
public override IObservable<Size> DoTransition(IObservable<double> progress, Size oldValue, Size newValue) |
||||
|
{ |
||||
|
return progress |
||||
|
.Select(p => |
||||
|
{ |
||||
|
var f = Easing.Ease(p); |
||||
|
return ((newValue - oldValue) * f) + oldValue; |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Reactive.Linq; |
||||
|
|
||||
|
namespace Avalonia.Animation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Vector"/> type.
|
||||
|
/// </summary>
|
||||
|
public class VectorTransition : Transition<Vector> |
||||
|
{ |
||||
|
/// <inheritdocs/>
|
||||
|
public override IObservable<Vector> DoTransition(IObservable<double> progress, Vector oldValue, Vector newValue) |
||||
|
{ |
||||
|
return progress |
||||
|
.Select(p => |
||||
|
{ |
||||
|
var f = Easing.Ease(p); |
||||
|
return ((newValue - oldValue) * f) + oldValue; |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Avalonia.Animation; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Styling; |
||||
|
using Avalonia.UnitTests; |
||||
|
using Avalonia.Data; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Avalonia.Animation.UnitTests |
||||
|
{ |
||||
|
public class AnimationIterationTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Check_Initial_Inter_and_Trailing_Delay_Values() |
||||
|
{ |
||||
|
var keyframe1 = new KeyFrame() |
||||
|
{ |
||||
|
Setters = |
||||
|
{ |
||||
|
new Setter(Border.WidthProperty, 200d), |
||||
|
}, |
||||
|
Cue = new Cue(1d) |
||||
|
}; |
||||
|
|
||||
|
var keyframe2 = new KeyFrame() |
||||
|
{ |
||||
|
Setters = |
||||
|
{ |
||||
|
new Setter(Border.WidthProperty, 100d), |
||||
|
}, |
||||
|
Cue = new Cue(0d) |
||||
|
}; |
||||
|
|
||||
|
var animation = new Animation() |
||||
|
{ |
||||
|
Duration = TimeSpan.FromSeconds(3), |
||||
|
Delay = TimeSpan.FromSeconds(3), |
||||
|
DelayBetweenIterations = TimeSpan.FromSeconds(3), |
||||
|
IterationCount = new IterationCount(2), |
||||
|
Children = |
||||
|
{ |
||||
|
keyframe2, |
||||
|
keyframe1 |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var border = new Border() |
||||
|
{ |
||||
|
Height = 100d, |
||||
|
Width = 100d |
||||
|
}; |
||||
|
|
||||
|
var clock = new TestClock(); |
||||
|
var animationRun = animation.RunAsync(border, clock); |
||||
|
|
||||
|
clock.Step(TimeSpan.Zero); |
||||
|
|
||||
|
// Initial Delay.
|
||||
|
clock.Step(TimeSpan.FromSeconds(1)); |
||||
|
Assert.Equal(border.Width, 0d); |
||||
|
|
||||
|
clock.Step(TimeSpan.FromSeconds(6)); |
||||
|
|
||||
|
// First Inter-Iteration delay.
|
||||
|
clock.Step(TimeSpan.FromSeconds(8)); |
||||
|
Assert.Equal(border.Width, 200d); |
||||
|
|
||||
|
// Trailing Delay should be non-existent.
|
||||
|
clock.Step(TimeSpan.FromSeconds(14)); |
||||
|
Assert.True(animationRun.Status == TaskStatus.RanToCompletion); |
||||
|
Assert.Equal(border.Width, 100d); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0"> |
||||
|
<PropertyGroup> |
||||
|
<TargetFrameworks>netcoreapp2.0</TargetFrameworks> |
||||
|
<OutputType>Library</OutputType> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="..\..\build\UnitTests.NetCore.targets" /> |
||||
|
<Import Project="..\..\build\Moq.props" /> |
||||
|
<Import Project="..\..\build\XUnit.props" /> |
||||
|
<Import Project="..\..\build\Rx.props" /> |
||||
|
<Import Project="..\..\build\Microsoft.Reactive.Testing.props" /> |
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj" /> |
||||
|
<ProjectReference Include="..\Avalonia.UnitTests\Avalonia.UnitTests.csproj" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" /> |
||||
|
</ItemGroup> |
||||
|
</Project> |
||||
@ -0,0 +1,10 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System.Reflection; |
||||
|
using Xunit; |
||||
|
|
||||
|
[assembly: AssemblyTitle("Avalonia.Animation.UnitTests")] |
||||
|
|
||||
|
// Don't run tests in parallel.
|
||||
|
[assembly: CollectionBehavior(DisableTestParallelization = true)] |
||||
@ -0,0 +1,28 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Avalonia.Animation.UnitTests |
||||
|
{ |
||||
|
internal class TestClock : IClock, IDisposable |
||||
|
{ |
||||
|
private IObserver<TimeSpan> _observer; |
||||
|
|
||||
|
public PlayState PlayState { get; set; } = PlayState.Run; |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_observer?.OnCompleted(); |
||||
|
} |
||||
|
|
||||
|
public void Step(TimeSpan time) |
||||
|
{ |
||||
|
_observer?.OnNext(time); |
||||
|
} |
||||
|
|
||||
|
public IDisposable Subscribe(IObserver<TimeSpan> observer) |
||||
|
{ |
||||
|
_observer = observer; |
||||
|
return this; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue