Browse Source

Merge pull request #5886 from AvaloniaUI/animations/tryfix-scb-localvalue

Revamp IBrush animator selection + Stop SCBAnimator from setting localvalues on animation apply
pull/5909/head
Dan Walmsley 5 years ago
committed by GitHub
parent
commit
a9f34e67b7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      src/Avalonia.Animation/Animation.cs
  2. 8
      src/Avalonia.Animation/Animators/Animator`1.cs
  3. 69
      src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs
  4. 7
      src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs
  5. 71
      src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs
  6. 2
      src/Avalonia.Visuals/Media/Brush.cs
  7. 3
      src/Avalonia.Visuals/Media/SolidColorBrush.cs

11
src/Avalonia.Animation/Animation.cs

@ -209,6 +209,17 @@ namespace Avalonia.Animation
( prop => typeof(decimal).IsAssignableFrom(prop.PropertyType), typeof(DecimalAnimator) ),
};
/// <summary>
/// Registers a <see cref="Animator{T}"/> that can handle
/// a value type that matches the specified condition.
/// </summary>
/// <param name="condition">
/// The condition to which the <see cref="Animator{T}"/>
/// is to be activated and used.
/// </param>
/// <typeparam name="TAnimator">
/// The type of the animator to instantiate.
/// </typeparam>
public static void RegisterAnimator<TAnimator>(Func<AvaloniaProperty, bool> condition)
where TAnimator : IAnimator
{

8
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<T> instance)
{
return control.Bind((AvaloniaProperty<T>)Property, instance, BindingPriority.Animation);
}
/// <summary>
/// Runs the KeyFrames Animation.
/// </summary>
@ -116,7 +121,8 @@ namespace Avalonia.Animation.Animators
clock ?? control.Clock ?? Clock.GlobalClock,
onComplete,
InterpolationHandler);
return control.Bind<T>((AvaloniaProperty<T>)Property, instance, BindingPriority.Animation);
return BindAnimation(control, instance);
}
/// <summary>

69
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
{
/// <summary>
/// Animator that handles all animations on properties
/// with <see cref="IBrush"/> as their type and
/// redirect them to the properly registered
/// animators in this class.
/// </summary>
public class BaseBrushAnimator : Animator<IBrush>
{
private IAnimator _targetAnimator;
private static readonly List<(Func<Type, bool> Match, Type AnimatorType)> _brushAnimators =
new List<(Func<Type, bool> Match, Type AnimatorType)>();
/// <summary>
/// Register an <see cref="Animator{T}"/> that handles a specific
/// <see cref="IBrush"/>'s descendant value type.
/// </summary>
/// <param name="condition">
/// The condition to which the <see cref="Animator{T}"/>
/// is to be activated and used.
/// </param>
/// <typeparam name="TAnimator">
/// The type of the animator to instantiate.
/// </typeparam>
public static void RegisterBrushAnimator<TAnimator>(Func<Type, bool> condition)
where TAnimator : IAnimator
{
_brushAnimators.Insert(0, (condition, typeof(TAnimator)));
}
/// <inheritdoc/>
public override IDisposable Apply(Animation animation, Animatable control, IClock clock,
IObservable<bool> 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;
}
/// <inheritdoc/>
public override IBrush Interpolate(double progress, IBrush oldValue, IBrush newValue) => null;
}
}

7
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;

71
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
{
/// <summary>
/// Animator that handles <see cref="SolidColorBrush"/>.
/// Animator that handles <see cref="SolidColorBrush"/> values.
/// </summary>
public class SolidColorBrushAnimator : Animator<SolidColorBrush>
public class ISolidColorBrushAnimator : Animator<ISolidColorBrush>
{
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<bool> match, Action onComplete)
public override IDisposable BindAnimation(Animatable control, IObservable<ISolidColorBrush> 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<IBrush>)Property, instance, BindingPriority.Animation);
}
}
[Obsolete]
public class SolidColorBrushAnimator : Animator<SolidColorBrush>
{
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;
}
}

2
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<BaseBrushAnimator>(prop => typeof(IBrush).IsAssignableFrom(prop.PropertyType));
AffectsRender<Brush>(OpacityProperty);
}

3
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<SolidColorBrushAnimator>(prop => typeof(IBrush).IsAssignableFrom(prop.PropertyType));
BaseBrushAnimator.RegisterBrushAnimator<ISolidColorBrushAnimator>(match => typeof(ISolidColorBrush).IsAssignableFrom(match));
AffectsRender<SolidColorBrush>(ColorProperty);
}

Loading…
Cancel
Save