Improved internal AnimationBuilder architecture

This commit is contained in:
Sergio Pedri 2020-12-21 17:46:06 +01:00
Родитель 9105cf0313
Коммит 1d3de3c1e5
8 изменённых файлов: 125 добавлений и 122 удалений

Просмотреть файл

@ -15,6 +15,8 @@ using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
#nullable enable
namespace Microsoft.Toolkit.Uwp.UI.Animations
{
/// <inheritdoc cref="AnimationBuilder"/>
@ -35,13 +37,15 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
where T : unmanaged
{
/// <inheritdoc/>
public CompositionAnimation GetAnimation(Visual visual)
public CompositionAnimation GetAnimation(CompositionObject targetHint, out CompositionObject? target)
{
CompositionEasingFunction easingFunction = visual.Compositor.CreateCubicBezierEasingFunction(EasingType, EasingMode);
CompositionEasingFunction easingFunction = targetHint.Compositor.CreateCubicBezierEasingFunction(EasingType, EasingMode);
target = null;
if (typeof(T) == typeof(bool))
{
return visual.Compositor.CreateBooleanKeyFrameAnimation(
return targetHint.Compositor.CreateBooleanKeyFrameAnimation(
Property,
GetToAs<bool>(),
GetFromAs<bool>(),
@ -50,7 +54,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
else if (typeof(T) == typeof(float))
{
return visual.Compositor.CreateScalarKeyFrameAnimation(
return targetHint.Compositor.CreateScalarKeyFrameAnimation(
Property,
GetToAs<float>(),
GetFromAs<float>(),
@ -60,17 +64,17 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
else if (typeof(T) == typeof(double))
{
return visual.Compositor.CreateScalarKeyFrameAnimation(
return targetHint.Compositor.CreateScalarKeyFrameAnimation(
Property,
(float)GetToAs<double>(),
(float)GetFromAs<double>(),
(float?)GetFromAs<double>(),
Delay,
Duration,
easingFunction);
}
else if (typeof(T) == typeof(Vector2))
{
return visual.Compositor.CreateVector2KeyFrameAnimation(
return targetHint.Compositor.CreateVector2KeyFrameAnimation(
Property,
GetToAs<Vector2>(),
GetFromAs<Vector2>(),
@ -80,7 +84,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
else if (typeof(T) == typeof(Vector3))
{
return visual.Compositor.CreateVector3KeyFrameAnimation(
return targetHint.Compositor.CreateVector3KeyFrameAnimation(
Property,
GetToAs<Vector3>(),
GetFromAs<Vector3>(),
@ -90,7 +94,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
else if (typeof(T) == typeof(Vector4))
{
return visual.Compositor.CreateVector4KeyFrameAnimation(
return targetHint.Compositor.CreateVector4KeyFrameAnimation(
Property,
GetToAs<Vector4>(),
GetFromAs<Vector4>(),
@ -100,7 +104,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
else if (typeof(T) == typeof(Color))
{
return visual.Compositor.CreateColorKeyFrameAnimation(
return targetHint.Compositor.CreateColorKeyFrameAnimation(
Property,
GetToAs<Color>(),
GetFromAs<Color>(),
@ -110,7 +114,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
else if (typeof(T) == typeof(Quaternion))
{
return visual.Compositor.CreateQuaternionKeyFrameAnimation(
return targetHint.Compositor.CreateQuaternionKeyFrameAnimation(
Property,
GetToAs<Quaternion>(),
GetFromAs<Quaternion>(),
@ -125,13 +129,13 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
/// <inheritdoc/>
public Timeline GetAnimation(UIElement element)
public Timeline GetAnimation(DependencyObject targetHint)
{
EasingFunctionBase easingFunction = EasingType.ToEasingFunction(EasingMode);
EasingFunctionBase? easingFunction = EasingType.ToEasingFunction(EasingMode);
if (typeof(T) == typeof(float))
{
return element.CreateDoubleAnimation(
return targetHint.CreateDoubleAnimation(
Property,
GetToAs<float>(),
GetFromAs<float>(),
@ -142,7 +146,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
else if (typeof(T) == typeof(double))
{
return element.CreateDoubleAnimation(
return targetHint.CreateDoubleAnimation(
Property,
GetToAs<double>(),
GetFromAs<double>(),
@ -153,7 +157,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
else if (typeof(T) == typeof(Point))
{
return element.CreatePointAnimation(
return targetHint.CreatePointAnimation(
Property,
GetToAs<Point>(),
GetFromAs<Point>(),
@ -164,7 +168,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
else if (typeof(T) == typeof(Color))
{
return element.CreateColorAnimation(
return targetHint.CreateColorAnimation(
Property,
GetToAs<Color>(),
GetFromAs<Color>(),
@ -214,30 +218,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
}
/// <summary>
/// A model representing a specified composition double animation for a target <see cref="CompositionObject"/>.
/// </summary>
private sealed record CompositionDoubleAnimation(
CompositionObject Target,
string Property,
float To,
float? From,
TimeSpan Delay,
TimeSpan Duration,
EasingType EasingType,
EasingMode EasingMode)
: ICompositionAnimation
{
/// <inheritdoc/>
public void StartAnimation()
{
CompositionEasingFunction easingFunction = Target.Compositor.CreateCubicBezierEasingFunction(EasingType, EasingMode);
ScalarKeyFrameAnimation animation = Target.Compositor.CreateScalarKeyFrameAnimation(Property, To, From, Delay, Duration, easingFunction);
Target.StartAnimation(Property, animation);
}
}
/// <summary>
/// A model representing a specified composition scalar animation factory targeting a clip.
/// </summary>
@ -252,11 +232,40 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
: ICompositionAnimationFactory
{
/// <inheritdoc/>
public CompositionAnimation GetAnimation(Visual visual)
public CompositionAnimation GetAnimation(CompositionObject targetHint, out CompositionObject? target)
{
Visual visual = (Visual)targetHint;
InsetClip clip = visual.Clip as InsetClip ?? (InsetClip)(visual.Clip = visual.Compositor.CreateInsetClip());
CompositionEasingFunction easingFunction = clip.Compositor.CreateCubicBezierEasingFunction(EasingType, EasingMode);
ScalarKeyFrameAnimation animation = visual.Compositor.CreateScalarKeyFrameAnimation(Property, To, From, Delay, Duration, easingFunction);
ScalarKeyFrameAnimation animation = clip.Compositor.CreateScalarKeyFrameAnimation(Property, To, From, Delay, Duration, easingFunction);
target = clip;
return animation;
}
}
/// <summary>
/// A model representing a specified composition double animation for a target <see cref="CompositionObject"/>.
/// </summary>
private sealed record CompositionDoubleAnimation(
CompositionObject Target,
string Property,
float To,
float? From,
TimeSpan Delay,
TimeSpan Duration,
EasingType EasingType,
EasingMode EasingMode)
: ICompositionAnimationFactory
{
/// <inheritdoc/>
public CompositionAnimation GetAnimation(CompositionObject targetHint, out CompositionObject? target)
{
CompositionEasingFunction easingFunction = Target.Compositor.CreateCubicBezierEasingFunction(EasingType, EasingMode);
ScalarKeyFrameAnimation animation = Target.Compositor.CreateScalarKeyFrameAnimation(Property, To, From, Delay, Duration, easingFunction);
target = Target;
return animation;
}
@ -276,9 +285,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
: IXamlAnimationFactory
{
/// <inheritdoc/>
public Timeline GetAnimation(UIElement element)
public Timeline GetAnimation(DependencyObject targetHint)
{
CompositeTransform transform = element.GetTransform<CompositeTransform>();
CompositeTransform transform = ((UIElement)targetHint).GetTransform<CompositeTransform>();
return transform.CreateDoubleAnimation(Property, To, From, Duration, Delay, EasingType.ToEasingFunction(EasingMode));
}
@ -292,9 +301,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
/// <summary>
/// Gets a <see cref="Timeline"/> instance representing the animation to start.
/// </summary>
/// <param name="element">The target <see cref="UIElement"/> instance to animate.</param>
/// <param name="targetHint">The suggested target <see cref="DependencyObject"/> instance to animate.</param>
/// <returns>A <see cref="Timeline"/> instance with the specified animation.</returns>
Timeline GetAnimation(UIElement element);
Timeline GetAnimation(DependencyObject targetHint);
}
/// <summary>
@ -305,20 +314,15 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
/// <summary>
/// Gets a <see cref="CompositionAnimation"/> instance representing the animation to start.
/// </summary>
/// <param name="visual">The target <see cref="Visual"/> instance to animate.</param>
/// <param name="targetHint">The suggested target <see cref="CompositionObject"/> instance to animate.</param>
/// <param name="target">An optional <see cref="CompositionObject"/> instance to animate instead of the suggested one.</param>
/// <returns>A <see cref="CompositionAnimation"/> instance with the specified animation.</returns>
CompositionAnimation GetAnimation(Visual visual);
}
/// <summary>
/// An interface for custom external composition animations.
/// </summary>
internal interface ICompositionAnimation
{
/// <summary>
/// Starts a <see cref="CompositionAnimation"/> with some embedded parameters.
/// </summary>
void StartAnimation();
/// <remarks>
/// The separate <paramref name="target"/> parameter is needed because unlike with XAML animations, composition animations
/// can't store the target instance internally, and need to be started on the target object directly. This means that custom
/// animation factories that want to target an external object need to return that object separately to inform the callers.
/// </remarks>
CompositionAnimation GetAnimation(CompositionObject targetHint, out CompositionObject? target);
}
}
}

Просмотреть файл

@ -14,11 +14,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
/// </summary>
public sealed partial class AnimationBuilder
{
/// <summary>
/// The list of <see cref="ICompositionAnimation"/> instances representing animations to run.
/// </summary>
private readonly List<ICompositionAnimation> compositionAnimations = new();
/// <summary>
/// The list of <see cref="ICompositionAnimationFactory"/> instances representing factories for composition animations to run.
/// </summary>

Просмотреть файл

@ -82,7 +82,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
easingType,
easingMode);
this.compositionAnimations.Add(animation);
this.compositionAnimationFactories.Add(animation);
return this;
}
@ -93,27 +93,25 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
/// <param name="element">The target <see cref="UIElement"/> to animate.</param>
public void Start(UIElement element)
{
if (this.compositionAnimations.Count > 0)
{
foreach (var animation in this.compositionAnimations)
{
animation.StartAnimation();
}
}
if (this.compositionAnimationFactories.Count > 0)
{
ElementCompositionPreview.SetIsTranslationEnabled(element, true);
Visual visual = ElementCompositionPreview.GetElementVisual(element);
CompositionAnimationGroup group = visual.Compositor.CreateAnimationGroup();
foreach (var factory in this.compositionAnimationFactories)
{
group.Add(factory.GetAnimation(visual));
}
var animation = factory.GetAnimation(visual, out var target);
visual.StartAnimationGroup(group);
if (target is null)
{
visual.StartAnimation(animation.Target, animation);
}
else
{
target.StartAnimation(animation.Target, animation);
}
}
}
if (this.xamlAnimationFactories.Count > 0)
@ -140,30 +138,29 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
compositionTask = Task.CompletedTask,
xamlTask = Task.CompletedTask;
if (this.compositionAnimationFactories.Count > 0 ||
this.compositionAnimations.Count > 0)
if (this.compositionAnimationFactories.Count > 0)
{
ElementCompositionPreview.SetIsTranslationEnabled(element, true);
Visual visual = ElementCompositionPreview.GetElementVisual(element);
CompositionScopedBatch batch = visual.Compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
TaskCompletionSource<object?> taskCompletionSource = new();
batch.Completed += (_, _) => taskCompletionSource.SetResult(null);
foreach (var animation in this.compositionAnimations)
{
animation.StartAnimation();
}
ElementCompositionPreview.SetIsTranslationEnabled(element, true);
CompositionAnimationGroup group = visual.Compositor.CreateAnimationGroup();
foreach (var factory in this.compositionAnimationFactories)
{
group.Add(factory.GetAnimation(visual));
}
var animation = factory.GetAnimation(visual, out var target);
visual.StartAnimationGroup(group);
if (target is null)
{
visual.StartAnimation(animation.Target, animation);
}
else
{
target.StartAnimation(animation.Target, animation);
}
}
batch.End();

Просмотреть файл

@ -10,6 +10,8 @@ using Microsoft.Toolkit.Uwp.UI.Animations.Extensions;
using Windows.UI;
using Windows.UI.Composition;
#nullable enable
namespace Microsoft.Toolkit.Uwp.UI.Animations
{
/// <inheritdoc cref="NormalizedKeyFrameAnimationBuilder{T}"/>
@ -19,14 +21,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
/// Gets a <see cref="CompositionAnimation"/> instance representing the animation to start.
/// </summary>
/// <typeparam name="TKeyFrame">The type of keyframes being used to define the animation.</typeparam>
/// <param name="visual">The target <see cref="Visual"/> instance to animate.</param>
/// <param name="target">The target <see cref="CompositionObject"/> instance to animate.</param>
/// <param name="property">The target property to animate.</param>
/// <param name="delay">The optional initial delay for the animation.</param>
/// <param name="duration">The animation duration.</param>
/// <param name="keyFrames">The list of keyframes to use to build the animation.</param>
/// <returns>A <see cref="CompositionAnimation"/> instance with the specified animation.</returns>
public static CompositionAnimation GetAnimation<TKeyFrame>(
Visual visual,
CompositionObject target,
string property,
TimeSpan? delay,
TimeSpan duration,
@ -37,7 +39,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
if (typeof(T) == typeof(bool))
{
BooleanKeyFrameAnimation boolAnimation = visual.Compositor.CreateBooleanKeyFrameAnimation();
BooleanKeyFrameAnimation boolAnimation = target.Compositor.CreateBooleanKeyFrameAnimation();
foreach (var keyFrame in keyFrames)
{
@ -50,98 +52,98 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
else if (typeof(T) == typeof(float))
{
ScalarKeyFrameAnimation scalarAnimation = visual.Compositor.CreateScalarKeyFrameAnimation();
ScalarKeyFrameAnimation scalarAnimation = target.Compositor.CreateScalarKeyFrameAnimation();
foreach (var keyFrame in keyFrames)
{
scalarAnimation.InsertKeyFrame(
(float)keyFrame.GetNormalizedProgress(duration),
keyFrame.GetValueAs<float>(),
visual.Compositor.CreateCubicBezierEasingFunction(keyFrame.EasingType, keyFrame.EasingMode));
target.Compositor.CreateCubicBezierEasingFunction(keyFrame.EasingType, keyFrame.EasingMode));
}
animation = scalarAnimation;
}
else if (typeof(T) == typeof(double))
{
ScalarKeyFrameAnimation scalarAnimation = visual.Compositor.CreateScalarKeyFrameAnimation();
ScalarKeyFrameAnimation scalarAnimation = target.Compositor.CreateScalarKeyFrameAnimation();
foreach (var keyFrame in keyFrames)
{
scalarAnimation.InsertKeyFrame(
(float)keyFrame.GetNormalizedProgress(duration),
(float)keyFrame.GetValueAs<double>(),
visual.Compositor.CreateCubicBezierEasingFunction(keyFrame.EasingType, keyFrame.EasingMode));
target.Compositor.CreateCubicBezierEasingFunction(keyFrame.EasingType, keyFrame.EasingMode));
}
animation = scalarAnimation;
}
else if (typeof(T) == typeof(Vector2))
{
Vector2KeyFrameAnimation vector2Animation = visual.Compositor.CreateVector2KeyFrameAnimation();
Vector2KeyFrameAnimation vector2Animation = target.Compositor.CreateVector2KeyFrameAnimation();
foreach (var keyFrame in keyFrames)
{
vector2Animation.InsertKeyFrame(
(float)keyFrame.GetNormalizedProgress(duration),
keyFrame.GetValueAs<Vector2>(),
visual.Compositor.CreateCubicBezierEasingFunction(keyFrame.EasingType, keyFrame.EasingMode));
target.Compositor.CreateCubicBezierEasingFunction(keyFrame.EasingType, keyFrame.EasingMode));
}
animation = vector2Animation;
}
else if (typeof(T) == typeof(Vector3))
{
Vector3KeyFrameAnimation vector3Animation = visual.Compositor.CreateVector3KeyFrameAnimation();
Vector3KeyFrameAnimation vector3Animation = target.Compositor.CreateVector3KeyFrameAnimation();
foreach (var keyFrame in keyFrames)
{
vector3Animation.InsertKeyFrame(
(float)keyFrame.GetNormalizedProgress(duration),
keyFrame.GetValueAs<Vector3>(),
visual.Compositor.CreateCubicBezierEasingFunction(keyFrame.EasingType, keyFrame.EasingMode));
target.Compositor.CreateCubicBezierEasingFunction(keyFrame.EasingType, keyFrame.EasingMode));
}
animation = vector3Animation;
}
else if (typeof(T) == typeof(Vector4))
{
Vector4KeyFrameAnimation vector4Animation = visual.Compositor.CreateVector4KeyFrameAnimation();
Vector4KeyFrameAnimation vector4Animation = target.Compositor.CreateVector4KeyFrameAnimation();
foreach (var keyFrame in keyFrames)
{
vector4Animation.InsertKeyFrame(
(float)keyFrame.GetNormalizedProgress(duration),
keyFrame.GetValueAs<Vector4>(),
visual.Compositor.CreateCubicBezierEasingFunction(keyFrame.EasingType, keyFrame.EasingMode));
target.Compositor.CreateCubicBezierEasingFunction(keyFrame.EasingType, keyFrame.EasingMode));
}
animation = vector4Animation;
}
else if (typeof(T) == typeof(Color))
{
ColorKeyFrameAnimation colorAnimation = visual.Compositor.CreateColorKeyFrameAnimation();
ColorKeyFrameAnimation colorAnimation = target.Compositor.CreateColorKeyFrameAnimation();
foreach (var keyFrame in keyFrames)
{
colorAnimation.InsertKeyFrame(
(float)keyFrame.GetNormalizedProgress(duration),
keyFrame.GetValueAs<Color>(),
visual.Compositor.CreateCubicBezierEasingFunction(keyFrame.EasingType, keyFrame.EasingMode));
target.Compositor.CreateCubicBezierEasingFunction(keyFrame.EasingType, keyFrame.EasingMode));
}
animation = colorAnimation;
}
else if (typeof(T) == typeof(Quaternion))
{
QuaternionKeyFrameAnimation quaternionAnimation = visual.Compositor.CreateQuaternionKeyFrameAnimation();
QuaternionKeyFrameAnimation quaternionAnimation = target.Compositor.CreateQuaternionKeyFrameAnimation();
foreach (var keyFrame in keyFrames)
{
quaternionAnimation.InsertKeyFrame(
(float)keyFrame.GetNormalizedProgress(duration),
keyFrame.GetValueAs<Quaternion>(),
visual.Compositor.CreateCubicBezierEasingFunction(keyFrame.EasingType, keyFrame.EasingMode));
target.Compositor.CreateCubicBezierEasingFunction(keyFrame.EasingType, keyFrame.EasingMode));
}
animation = quaternionAnimation;
@ -178,10 +180,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
/// <inheritdoc/>
public CompositionAnimation GetAnimation(Visual visual)
public CompositionAnimation GetAnimation(CompositionObject targetHint, out CompositionObject? target)
{
target = null;
return GetAnimation(
visual,
targetHint,
this.property,
this.delay,
this.duration,

Просмотреть файл

@ -27,10 +27,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
/// <inheritdoc/>
public Timeline GetAnimation(UIElement element)
public Timeline GetAnimation(DependencyObject targetHint)
{
return TimedKeyFrameAnimationBuilder<T>.GetAnimation(
element,
targetHint,
this.property,
this.delay,
this.duration,

Просмотреть файл

@ -5,6 +5,8 @@
using System;
using Windows.UI.Composition;
#nullable enable
namespace Microsoft.Toolkit.Uwp.UI.Animations
{
/// <inheritdoc cref="TimedKeyFrameAnimationBuilder{T}"/>
@ -26,14 +28,16 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
/// <inheritdoc/>
public CompositionAnimation GetAnimation(Visual visual)
public CompositionAnimation GetAnimation(CompositionObject targetHint, out CompositionObject? target)
{
target = null;
// We can retrieve the total duration from the last timed keyframe, and then set
// this as the target duration and use it to normalize the keyframe progresses.
TimeSpan duration = this.keyFrames[this.keyFrames.Count - 1].GetTimedProgress(default);
return NormalizedKeyFrameAnimationBuilder<T>.GetAnimation(
visual,
targetHint,
this.property,
this.delay,
duration,

Просмотреть файл

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using Microsoft.Toolkit.Diagnostics;
using Microsoft.Toolkit.Uwp.UI.Animations.Extensions;
using Windows.Foundation;
@ -22,14 +21,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
/// Gets a <see cref="Timeline"/> instance representing the animation to start.
/// </summary>
/// <typeparam name="TKeyFrame">The type of keyframes being used to define the animation.</typeparam>
/// <param name="element">The target <see cref="UIElement"/> instance to animate.</param>
/// <param name="target">The target <see cref="DependencyObject"/> instance to animate.</param>
/// <param name="property">The target property to animate.</param>
/// <param name="delay">The optional initial delay for the animation.</param>
/// <param name="duration">The animation duration.</param>
/// <param name="keyFrames">The list of keyframes to use to build the animation.</param>
/// <returns>A <see cref="Timeline"/> instance with the specified animation.</returns>
public static Timeline GetAnimation<TKeyFrame>(
UIElement element,
DependencyObject target,
string property,
TimeSpan? delay,
TimeSpan duration,
@ -109,7 +108,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
animation.BeginTime = delay;
Storyboard.SetTarget(animation, element);
Storyboard.SetTarget(animation, target);
Storyboard.SetTargetProperty(animation, property);
return animation;
@ -130,10 +129,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
/// <inheritdoc/>
public Timeline GetAnimation(UIElement element)
public Timeline GetAnimation(DependencyObject targetHint)
{
return GetAnimation(
element,
targetHint,
this.property,
this.delay,
default,

Просмотреть файл

@ -30,8 +30,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Media.Animations
return builder.DoubleAnimation(
effect.Brush,
$"{effect.Id}.{nameof(GaussianBlurEffect.BlurAmount)}",
From,
To,
From,
Delay ?? delayHint,
Duration ?? durationHint.GetValueOrDefault(),
EasingType ?? easingTypeHint ?? DefaultEasingType,