diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs
index b06586dd72..f630ff05dc 100644
--- a/src/Avalonia.Animation/Animatable.cs
+++ b/src/Avalonia.Animation/Animatable.cs
@@ -27,19 +27,25 @@ namespace Avalonia.Animation
///
/// Defines the property.
///
- public static readonly StyledProperty AnimationPlayStateProperty =
- AvaloniaProperty.Register(nameof(AnimationPlayState));
+ public static readonly DirectProperty AnimationPlayStateProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(AnimationPlayState),
+ o => o.AnimationPlayState,
+ (o, v) => o.AnimationPlayState = v);
///
/// Gets or sets the state of the animation for this
/// control.
///
public AnimationPlayState AnimationPlayState
- {
- get { return GetValue(AnimationPlayStateProperty); }
- set { SetValue(AnimationPlayStateProperty, value); }
+ {
+ get { return _animationPlayState; }
+ set { SetAndRaise(AnimationPlayStateProperty, ref _animationPlayState, value); }
+
}
+ private AnimationPlayState _animationPlayState;
+
///
/// Defines the property.
///
diff --git a/src/Avalonia.Animation/ColorInterpolationMode.cs b/src/Avalonia.Animation/ColorInterpolationMode.cs
new file mode 100644
index 0000000000..bfe4e5080e
--- /dev/null
+++ b/src/Avalonia.Animation/ColorInterpolationMode.cs
@@ -0,0 +1,34 @@
+namespace Avalonia.Animation
+{
+ ///
+ /// Defines the intermediate color space
+ /// in which the color interpolation will be done.
+ ///
+ public enum ColorInterpolationMode
+ {
+ ///
+ /// Premultiply the alpha component and interpolate from RGB color space.
+ /// Fast but relatively accurate, perception-wise.
+ ///
+ PremultipliedRGB,
+
+ ///
+ /// Directly interpolate from RGB color space.
+ /// Fastest but the least accurate, perception-wise.
+ ///
+ RGB,
+
+ ///
+ /// Converts RGB into HSV color space and interpolates.
+ /// Slow but accurate, perception-wise.
+ ///
+ HSV,
+
+ ///
+ /// Converts RGB into HSV color space and interpolates.
+ /// Slowest but the most accurate, perception-wise.
+ ///
+ LAB,
+
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Animation/Keyframes/KeyFrames.cs b/src/Avalonia.Animation/Keyframes/KeyFrames.cs
index a31becaccf..814a67be14 100644
--- a/src/Avalonia.Animation/Keyframes/KeyFrames.cs
+++ b/src/Avalonia.Animation/Keyframes/KeyFrames.cs
@@ -37,6 +37,7 @@ namespace Avalonia.Animation.Keyframes
return obsMatch
.Where(p => p == true)
+ // Ignore triggers when global timers are paused.
.Where(p=> Timing.GetGlobalPlayState() != AnimationPlayState.Paused)
.Subscribe(_ =>
{
diff --git a/src/Avalonia.Animation/Timing.cs b/src/Avalonia.Animation/Timing.cs
index fc75e2c7c3..39caeb52cc 100644
--- a/src/Avalonia.Animation/Timing.cs
+++ b/src/Avalonia.Animation/Timing.cs
@@ -143,7 +143,7 @@ namespace Avalonia.Animation
/// 0 being the start and 1 being the end. The observable is guaranteed to fire 0
/// immediately on subscribe and 1 at the end of the duration.
///
- public static IObservable GetTransitionsTimer(Animatable control, TimeSpan duration, TimeSpan delay)
+ public static IObservable GetTransitionsTimer(Animatable control, TimeSpan duration, TimeSpan delay = default(TimeSpan))
{
var startTime = _transitionsFrameCount;
var _duration = (ulong)(duration.Ticks / Tick.Ticks);
diff --git a/src/Avalonia.Animation/Transitions/Transition.cs b/src/Avalonia.Animation/Transitions/Transition.cs
index 66fda063b6..c382118d81 100644
--- a/src/Avalonia.Animation/Transitions/Transition.cs
+++ b/src/Avalonia.Animation/Transitions/Transition.cs
@@ -11,7 +11,7 @@ namespace Avalonia.Animation.Transitions
///
/// Defines how a property should be animated using a transition.
///
- public abstract class Transition : ITransition
+ public abstract class Transition : AvaloniaObject, ITransition
{
private AvaloniaProperty _prop;
private Easing _easing;
@@ -59,7 +59,7 @@ namespace Avalonia.Animation.Transitions
public abstract IObservable DoTransition(IObservable progress, T oldValue, T newValue);
///
- public IDisposable Apply(Animatable control, object oldValue, object newValue)
+ public virtual IDisposable Apply(Animatable control, object oldValue, object newValue)
{
var transition = DoTransition(Timing.GetTransitionsTimer(control, Duration, TimeSpan.Zero), (T)oldValue, (T)newValue).Select(p => (object)p);
return control.Bind(Property, transition, Data.BindingPriority.Animation);
diff --git a/src/Avalonia.Visuals/Animation/Transitions/ColorTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/ColorTransition.cs
new file mode 100644
index 0000000000..118f17a427
--- /dev/null
+++ b/src/Avalonia.Visuals/Animation/Transitions/ColorTransition.cs
@@ -0,0 +1,81 @@
+// 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 Avalonia.Metadata;
+using System;
+using System.Reactive.Linq;
+using Avalonia.Media;
+using System.Numerics;
+
+namespace Avalonia.Animation.Transitions
+{
+ ///
+ /// Transition class that handles with types.
+ /// TODO: Must be refactored to handle object creation.
+ ///
+ public class ColorTransition : Transition
+ {
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty InterpolationModeProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(InterpolationMode),
+ o => o.InterpolationMode,
+ (o, v) => o.InterpolationMode = v);
+
+ ///
+ /// Gets or sets the state of the animation for this
+ /// control.
+ ///
+ public ColorInterpolationMode InterpolationMode
+ {
+ get { return _interpolationMode; }
+ set { SetAndRaise(InterpolationModeProperty, ref _interpolationMode, value); }
+ }
+
+ private ColorInterpolationMode _interpolationMode;
+
+ public override IObservable DoTransition(IObservable progress, SolidColorBrush oldValue, SolidColorBrush newValue)
+ {
+ var oldColor = new Vector4(oldValue.Color.A, oldValue.Color.R, oldValue.Color.G, oldValue.Color.B);
+ var newColor = new Vector4(newValue.Color.A, newValue.Color.R, newValue.Color.G, newValue.Color.B);
+ oldColor = oldColor / 255f;
+ newColor = newColor / 255f;
+ var deltaColor = newColor - oldColor;
+
+ switch (InterpolationMode)
+ {
+ case ColorInterpolationMode.PremultipliedRGB:
+
+ var premultOV = new Vector4(1, oldColor.W, oldColor.W, oldColor.W);
+ var premultNV = new Vector4(1, newColor.W, newColor.W, newColor.W);
+
+ oldColor *= premultOV;
+ newColor *= premultNV;
+
+ return progress
+ .Select(p =>
+ {
+ var time = (float)Easing.Ease(p);
+ var interpolatedColor = (deltaColor * time) + oldColor;
+ return new SolidColorBrush(Color.FromVector4(interpolatedColor, true));
+ });
+
+ case ColorInterpolationMode.RGB:
+ return progress
+ .Select(p =>
+ {
+ var time = (float)Easing.Ease(p);
+ var interpolatedColor = (deltaColor * time) + oldColor;
+ return new SolidColorBrush(Color.FromVector4(interpolatedColor, true));
+ });
+ default:
+ throw new NotImplementedException($"{Enum.GetName(typeof(ColorInterpolationMode), InterpolationMode)} color interpolation is not supported.");
+ }
+
+ }
+
+ }
+}
diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs
index cbf5a86fd6..f37d529944 100644
--- a/src/Avalonia.Visuals/Media/Color.cs
+++ b/src/Avalonia.Visuals/Media/Color.cs
@@ -4,6 +4,7 @@
using System;
using System.Globalization;
using System.Linq;
+using System.Numerics;
using System.Reflection;
namespace Avalonia.Media
@@ -81,6 +82,32 @@ namespace Avalonia.Media
);
}
+ ///
+ /// Creates a from a .
+ ///
+ /// The value.
+ /// True if the color vector is normalized floats, false if it's bytes (0-255)
+ /// The color.
+ public static Color FromVector4(Vector4 value, bool normalized)
+ {
+ if (normalized)
+ {
+ return new Color(
+ (byte)(value.W * 255),
+ (byte)(value.X * 255),
+ (byte)(value.Y * 255),
+ (byte)(value.Z * 255)
+ );
+ } else {
+ return new Color(
+ (byte)(value.W),
+ (byte)(value.X),
+ (byte)(value.Y),
+ (byte)(value.Z)
+ );
+ }
+
+ }
///
/// Parses a color string.
///