diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Animation/Animation.cs
index 05142532e9..c42153ec4f 100644
--- a/src/Avalonia.Animation/Animation.cs
+++ b/src/Avalonia.Animation/Animation.cs
@@ -209,6 +209,17 @@ namespace Avalonia.Animation
( prop => typeof(decimal).IsAssignableFrom(prop.PropertyType), typeof(DecimalAnimator) ),
};
+ ///
+ /// Registers a that can handle
+ /// a value type that matches the specified condition.
+ ///
+ ///
+ /// The condition to which the
+ /// is to be activated and used.
+ ///
+ ///
+ /// The type of the animator to instantiate.
+ ///
public static void RegisterAnimator(Func condition)
where TAnimator : IAnimator
{
diff --git a/src/Avalonia.Animation/Animators/Animator`1.cs b/src/Avalonia.Animation/Animators/Animator`1.cs
index 0660440e30..d784227620 100644
--- a/src/Avalonia.Animation/Animators/Animator`1.cs
+++ b/src/Avalonia.Animation/Animators/Animator`1.cs
@@ -104,6 +104,11 @@ namespace Avalonia.Animation.Animators
throw new Exception("Index time is out of keyframe time range.");
}
+ public virtual IDisposable BindAnimation(Animatable control, IObservable instance)
+ {
+ return control.Bind((AvaloniaProperty)Property, instance, BindingPriority.Animation);
+ }
+
///
/// Runs the KeyFrames Animation.
///
@@ -116,7 +121,8 @@ namespace Avalonia.Animation.Animators
clock ?? control.Clock ?? Clock.GlobalClock,
onComplete,
InterpolationHandler);
- return control.Bind((AvaloniaProperty)Property, instance, BindingPriority.Animation);
+
+ return BindAnimation(control, instance);
}
///
diff --git a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs
new file mode 100644
index 0000000000..508891fd72
--- /dev/null
+++ b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.Reactive.Disposables;
+using Avalonia.Logging;
+using Avalonia.Media;
+
+namespace Avalonia.Animation.Animators
+{
+ ///
+ /// Animator that handles all animations on properties
+ /// with as their type and
+ /// redirect them to the properly registered
+ /// animators in this class.
+ ///
+ public class BaseBrushAnimator : Animator
+ {
+ private IAnimator _targetAnimator;
+
+ private static readonly List<(Func Match, Type AnimatorType)> _brushAnimators =
+ new List<(Func Match, Type AnimatorType)>();
+
+ ///
+ /// Register an that handles a specific
+ /// 's descendant value type.
+ ///
+ ///
+ /// The condition to which the
+ /// is to be activated and used.
+ ///
+ ///
+ /// The type of the animator to instantiate.
+ ///
+ public static void RegisterBrushAnimator(Func condition)
+ where TAnimator : IAnimator
+ {
+ _brushAnimators.Insert(0, (condition, typeof(TAnimator)));
+ }
+
+ ///
+ public override IDisposable Apply(Animation animation, Animatable control, IClock clock,
+ IObservable match, Action onComplete)
+ {
+ foreach (var valueType in _brushAnimators)
+ {
+ if (!valueType.Match(this[0].Value.GetType())) continue;
+
+ _targetAnimator = (IAnimator)Activator.CreateInstance(valueType.AnimatorType);
+
+ foreach (var keyframe in this)
+ {
+ _targetAnimator.Add(keyframe);
+ }
+
+ _targetAnimator.Property = this.Property;
+
+ return _targetAnimator.Apply(animation, control, clock, match, onComplete);
+ }
+
+ Logger.TryGet(LogEventLevel.Error, LogArea.Animations)?.Log(
+ this,
+ "The animation's keyframe values didn't match any brush animators registered in BaseBrushAnimator.");
+
+ return Disposable.Empty;
+ }
+
+ ///
+ public override IBrush Interpolate(double progress, IBrush oldValue, IBrush newValue) => null;
+ }
+}
diff --git a/src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs
index 6d1f6c39bd..1686cafeb9 100644
--- a/src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs
+++ b/src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs
@@ -31,6 +31,11 @@ namespace Avalonia.Animation.Animators
}
public override Color Interpolate(double progress, Color oldValue, Color newValue)
+ {
+ return InterpolateCore(progress, oldValue, newValue);
+ }
+
+ internal static Color InterpolateCore(double progress, Color oldValue, Color newValue)
{
// normalize sRGB values.
var oldA = oldValue.A / 255d;
@@ -59,7 +64,7 @@ namespace Avalonia.Animation.Animators
var b = oldB + progress * (newB - oldB);
// convert back to sRGB in the [0..255] range
- a = a * 255d;
+ a *= 255d;
r = OECF_sRGB(r) * 255d;
g = OECF_sRGB(g) * 255d;
b = OECF_sRGB(b) * 255d;
diff --git a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs
index a8e618af27..cec96fecf8 100644
--- a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs
+++ b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs
@@ -1,71 +1,32 @@
using System;
-using System.Reactive.Disposables;
+using Avalonia.Data;
using Avalonia.Media;
using Avalonia.Media.Immutable;
namespace Avalonia.Animation.Animators
{
///
- /// Animator that handles .
+ /// Animator that handles values.
///
- public class SolidColorBrushAnimator : Animator
+ public class ISolidColorBrushAnimator : Animator
{
- private ColorAnimator _colorAnimator;
-
- private void InitializeColorAnimator()
+ public override ISolidColorBrush Interpolate(double progress, ISolidColorBrush oldValue, ISolidColorBrush newValue)
{
- _colorAnimator = new ColorAnimator();
-
- foreach (AnimatorKeyFrame keyframe in this)
- {
- _colorAnimator.Add(keyframe);
- }
-
- _colorAnimator.Property = SolidColorBrush.ColorProperty;
+ return new ImmutableSolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color));
}
- public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable match, Action onComplete)
+ public override IDisposable BindAnimation(Animatable control, IObservable instance)
{
- // Preprocess keyframe values to Color if the xaml parser converts them to ISCB.
- foreach (var keyframe in this)
- {
- if (keyframe.Value is ISolidColorBrush colorBrush)
- {
- keyframe.Value = colorBrush.Color;
- }
- else
- {
- return Disposable.Empty;
- }
- }
-
- SolidColorBrush finalTarget;
- var targetVal = control.GetValue(Property);
- if (targetVal is null)
- {
- finalTarget = new SolidColorBrush(Colors.Transparent);
- control.SetValue(Property, finalTarget);
- }
- else if (targetVal is ImmutableSolidColorBrush immutableSolidColorBrush)
- {
- finalTarget = new SolidColorBrush(immutableSolidColorBrush.Color);
- control.SetValue(Property, finalTarget);
- }
- else if (targetVal is ISolidColorBrush)
- {
- finalTarget = targetVal as SolidColorBrush;
- }
- else
- {
- return Disposable.Empty;
- }
-
- if (_colorAnimator == null)
- InitializeColorAnimator();
-
- return _colorAnimator.Apply(animation, finalTarget, clock ?? control.Clock, match, onComplete);
+ return control.Bind((AvaloniaProperty)Property, instance, BindingPriority.Animation);
+ }
+ }
+
+ [Obsolete]
+ public class SolidColorBrushAnimator : Animator
+ {
+ public override SolidColorBrush Interpolate(double progress, SolidColorBrush oldValue, SolidColorBrush newValue)
+ {
+ return new SolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color));
}
-
- public override SolidColorBrush Interpolate(double p, SolidColorBrush o, SolidColorBrush n) => null;
}
}
diff --git a/src/Avalonia.Visuals/Media/Brush.cs b/src/Avalonia.Visuals/Media/Brush.cs
index a19d5af8b7..fb03d19a4e 100644
--- a/src/Avalonia.Visuals/Media/Brush.cs
+++ b/src/Avalonia.Visuals/Media/Brush.cs
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel;
using Avalonia.Animation;
+using Avalonia.Animation.Animators;
namespace Avalonia.Media
{
@@ -21,6 +22,7 @@ namespace Avalonia.Media
static Brush()
{
+ Animation.Animation.RegisterAnimator(prop => typeof(IBrush).IsAssignableFrom(prop.PropertyType));
AffectsRender(OpacityProperty);
}
diff --git a/src/Avalonia.Visuals/Media/SolidColorBrush.cs b/src/Avalonia.Visuals/Media/SolidColorBrush.cs
index 8e30880489..fd94cbd214 100644
--- a/src/Avalonia.Visuals/Media/SolidColorBrush.cs
+++ b/src/Avalonia.Visuals/Media/SolidColorBrush.cs
@@ -1,4 +1,3 @@
-using Avalonia.Animation;
using Avalonia.Animation.Animators;
using Avalonia.Media.Immutable;
@@ -17,7 +16,7 @@ namespace Avalonia.Media
static SolidColorBrush()
{
- Animation.Animation.RegisterAnimator(prop => typeof(IBrush).IsAssignableFrom(prop.PropertyType));
+ BaseBrushAnimator.RegisterBrushAnimator(match => typeof(ISolidColorBrush).IsAssignableFrom(match));
AffectsRender(ColorProperty);
}