Browse Source

Implement animation setters using the Avalonia.Styling.Setter class. Enable custom animators through a static method on Animation that can be called from the a static constructor (example in Transform.cs).

pull/1594/head
Jeremy Koritzinsky 8 years ago
parent
commit
4b8c6df9eb
  1. 20
      samples/RenderTest/Pages/AnimationsPage.xaml
  2. 2
      samples/RenderTest/Pages/ClippingPage.xaml
  3. 33
      src/Avalonia.Animation/Animation.cs
  4. 17
      src/Avalonia.Animation/AnimationSetter.cs
  5. 18
      src/Avalonia.Animation/AnimatorAttribute.cs
  6. 21
      src/Avalonia.Animation/DoubleSetter.cs
  7. 2
      src/Avalonia.Controls/Control.cs
  8. 2
      src/Avalonia.Styling/Styling/IRequiresTemplateInSetter.cs
  9. 7
      src/Avalonia.Styling/Styling/Setter.cs
  10. 22
      src/Avalonia.Visuals/Animation/TransformSetter.cs
  11. 5
      src/Avalonia.Visuals/Media/Transform.cs

20
samples/RenderTest/Pages/AnimationsPage.xaml

@ -48,13 +48,13 @@
PlaybackDirection="AlternateReverse"
Easing="SineEaseInOut">
<KeyFrame Cue="20%">
<TransformSetter Property="RotateTransform.Angle" Value="45"/>
<Setter Property="RotateTransform.Angle" Value="45"/>
</KeyFrame>
<KeyFrame Cue="50%">
<TransformSetter Property="ScaleTransform.ScaleX" Value="1.5"/>
<Setter Property="ScaleTransform.ScaleX" Value="1.5"/>
</KeyFrame>
<KeyFrame Cue="80%">
<TransformSetter Property="RotateTransform.Angle" Value="120"/>
<Setter Property="RotateTransform.Angle" Value="120"/>
</KeyFrame>
</Animation>
</Style.Animations>
@ -63,8 +63,8 @@
<Style.Animations>
<Animation Duration="0:0:0.5" Easing="SineEaseInOut">
<KeyFrame Cue="50%">
<TransformSetter Property="ScaleTransform.ScaleX" Value="0.8"/>
<TransformSetter Property="ScaleTransform.ScaleY" Value="0.8"/>
<Setter Property="ScaleTransform.ScaleX" Value="0.8"/>
<Setter Property="ScaleTransform.ScaleY" Value="0.8"/>
</KeyFrame>
</Animation>
</Style.Animations>
@ -76,8 +76,8 @@
Easing="QuadraticEaseInOut"
RepeatCount="Loop">
<KeyFrame Cue="50%">
<TransformSetter Property="ScaleTransform.ScaleX" Value="0.8"/>
<TransformSetter Property="ScaleTransform.ScaleY" Value="0.8"/>
<Setter Property="ScaleTransform.ScaleX" Value="0.8"/>
<Setter Property="ScaleTransform.ScaleY" Value="0.8"/>
</KeyFrame>
</Animation>
</Style.Animations>
@ -86,7 +86,7 @@
<Style.Animations>
<Animation Duration="0:0:3" Easing="BounceEaseInOut">
<KeyFrame Cue="48%">
<TransformSetter Property="TranslateTransform.Y" Value="-100"/>
<Setter Property="TranslateTransform.Y" Value="-100"/>
</KeyFrame>
</Animation>
</Style.Animations>
@ -95,10 +95,10 @@
<Style.Animations>
<Animation Duration="0:0:3" Easing="CircularEaseInOut">
<KeyFrame Cue="25%">
<TransformSetter Property="SkewTransform.AngleX" Value="-20"/>
<Setter Property="SkewTransform.AngleX" Value="-20"/>
</KeyFrame>
<KeyFrame Cue="75%">
<TransformSetter Property="SkewTransform.AngleX" Value="20"/>
<Setter Property="SkewTransform.AngleX" Value="20"/>
</KeyFrame>
</Animation>
</Style.Animations>

2
samples/RenderTest/Pages/ClippingPage.xaml

@ -10,7 +10,7 @@
<Style.Animations>
<Animation Duration="0:0:2" RepeatCount="Loop">
<KeyFrame Cue="100%">
<TransformSetter Property="RotateTransform.Angle" Value="360"/>
<Setter Property="RotateTransform.Angle" Value="360"/>
</KeyFrame>
</Animation>
</Style.Animations>

33
src/Avalonia.Animation/Animation.cs

@ -18,6 +18,28 @@ namespace Avalonia.Animation
/// </summary>
public class Animation : AvaloniaList<KeyFrame>, IDisposable, IAnimation
{
private readonly static List<(Func<AvaloniaProperty, bool> Condition, Type Animator)> Animators = new List<(Func<AvaloniaProperty, bool>, Type)>
{
( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator) )
};
public static void RegisterAnimator<TAnimator>(Func<AvaloniaProperty, bool> condition)
where TAnimator: IAnimator
{
Animators.Insert(0, (condition, typeof(TAnimator)));
}
private static Type GetAnimatorType(AvaloniaProperty property)
{
foreach (var (condition, type) in Animators)
{
if (condition(property))
{
return type;
}
}
return null;
}
private bool _isChildrenChanged = false;
private List<IDisposable> _subscription = new List<IDisposable>();
@ -67,14 +89,13 @@ namespace Avalonia.Animation
{
foreach (var setter in keyframe)
{
var custAttr = setter.GetType()
.GetCustomAttributes()
.Where(p => p.GetType() == typeof(AnimatorAttribute));
var handler = GetAnimatorType(setter.Property);
if (!custAttr.Any())
throw new InvalidProgramException($"Type {setter.GetType()} doesn't have Animator attribute.");
if (handler == null)
{
throw new InvalidOperationException($"No animator registered for the property {setter.Property}. Add an animator to the Animation.Animators collection that matches this property to animate it.");
}
var handler = ((AnimatorAttribute)custAttr.First()).HandlerType;
if (!handlerList.Contains((handler, setter.Property)))
handlerList.Add((handler, setter.Property));

17
src/Avalonia.Animation/AnimationSetter.cs

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Reactive.Linq;
using System.Diagnostics;
using Avalonia.Animation.Utils;
using Avalonia.Data;
namespace Avalonia.Animation
{
public abstract class AnimationSetter : IAnimationSetter
{
public AvaloniaProperty Property { get; set; }
public object Value { get; set; }
}
}

18
src/Avalonia.Animation/AnimatorAttribute.cs

@ -1,18 +0,0 @@
using System;
namespace Avalonia.Animation
{
/// <summary>
/// Attribute for <see cref="IAnimationSetter"/> objects
/// that maps the setter to it's <see cref="Animator{T}"/>.
/// </summary>
public class AnimatorAttribute : Attribute
{
public Type HandlerType;
public AnimatorAttribute(Type handler)
{
this.HandlerType = handler;
}
}
}

21
src/Avalonia.Animation/DoubleSetter.cs

@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Reactive.Linq;
using System.Diagnostics;
using Avalonia.Animation.Utils;
using Avalonia.Data;
namespace Avalonia.Animation
{
/// <summary>
/// Setter that handles <see cref="double"/> properties
/// in the target.
/// </summary>
[Animator(typeof(DoubleAnimator))]
public class DoubleSetter : AnimationSetter
{
}
}

2
src/Avalonia.Controls/Control.cs

@ -31,7 +31,7 @@ namespace Avalonia.Controls
///
/// - A <see cref="Tag"/> property to allow user-defined data to be attached to the control.
/// </remarks>
public class Control : InputElement, IControl, INamed, ISupportInitialize, IVisualBrushInitialize, IRequiresTemplateInStyle
public class Control : InputElement, IControl, INamed, ISupportInitialize, IVisualBrushInitialize, IRequiresTemplateInSetter
{
/// <summary>
/// Defines the <see cref="FocusAdorner"/> property.

2
src/Avalonia.Styling/Styling/IRequiresTemplateInStyle.cs → src/Avalonia.Styling/Styling/IRequiresTemplateInSetter.cs

@ -8,7 +8,7 @@ namespace Avalonia.Styling
/// This is an interface for advanced scenarios to assist users in correct style development.
/// You as a user will not need to use this interface directly.
/// </summary>
public interface IRequiresTemplateInStyle
public interface IRequiresTemplateInSetter
{
}
}

7
src/Avalonia.Styling/Styling/Setter.cs

@ -5,6 +5,7 @@ using System;
using System.Reactive.Disposables;
using System.Reactive.Subjects;
using System.Reflection;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Metadata;
@ -19,7 +20,7 @@ namespace Avalonia.Styling
/// A <see cref="Setter"/> is used to set a <see cref="AvaloniaProperty"/> value on a
/// <see cref="AvaloniaObject"/> depending on a condition.
/// </remarks>
public class Setter : ISetter
public class Setter : ISetter, IAnimationSetter
{
private object _value;
@ -65,10 +66,10 @@ namespace Avalonia.Styling
set
{
if (value is IRequiresTemplateInStyle)
if (value is IRequiresTemplateInSetter)
{
throw new ArgumentException(
"Cannot assign a control to Style.Value. Wrap the control in a <Template>.",
"Cannot assign a control to Setter.Value. Wrap the control in a <Template>.",
"value");
}

22
src/Avalonia.Visuals/Animation/TransformSetter.cs

@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Reactive.Linq;
using System.Diagnostics;
using Avalonia.Animation.Utils;
using Avalonia.Data;
using Avalonia.Media;
namespace Avalonia.Animation
{
/// <summary>
/// Setter that handles <see cref="Transform"/> objects
/// in the target.
/// </summary>
[Animator(typeof(TransformAnimator))]
public class TransformSetter : AnimationSetter
{
}
}

5
src/Avalonia.Visuals/Media/Transform.cs

@ -12,6 +12,11 @@ namespace Avalonia.Media
/// </summary>
public abstract class Transform : Animatable
{
static Transform()
{
Animation.Animation.RegisterAnimator<TransformAnimator>(prop => typeof(Transform).IsAssignableFrom(prop.OwnerType));
}
/// <summary>
/// Raised when the transform changes.
/// </summary>

Loading…
Cancel
Save