Implementing a new animation execution mechanism

This commit is contained in:
polarboy 2024-09-29 14:25:21 +08:00
parent 03422c53a6
commit 28abd64b04
7 changed files with 76 additions and 70 deletions

View File

@ -2,6 +2,7 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Transformation;
namespace AtomUI.Controls.Primitives; namespace AtomUI.Controls.Primitives;
@ -9,10 +10,10 @@ public class MotionActorControl : Decorator
{ {
#region #region
public static readonly StyledProperty<ITransform?> MotionTransformProperty = public static readonly StyledProperty<TransformOperations?> MotionTransformProperty =
AvaloniaProperty.Register<MotionActorControl, ITransform?>(nameof(MotionTransform)); AvaloniaProperty.Register<MotionActorControl, TransformOperations?>(nameof(MotionTransform));
public ITransform? MotionTransform public TransformOperations? MotionTransform
{ {
get => GetValue(MotionTransformProperty); get => GetValue(MotionTransformProperty);
set => SetValue(MotionTransformProperty, value); set => SetValue(MotionTransformProperty, value);
@ -58,6 +59,7 @@ public class MotionActorControl : Decorator
ChildProperty.Changed ChildProperty.Changed
.AddClassHandler<MotionActorControl>((x, _) => x.HandleChildChanged()); .AddClassHandler<MotionActorControl>((x, _) => x.HandleChildChanged());
AffectsRender<MotionActorControl>(MotionTransformProperty);
} }
private void HandleLayoutTransformChanged(AvaloniaPropertyChangedEventArgs e) private void HandleLayoutTransformChanged(AvaloniaPropertyChangedEventArgs e)
@ -108,7 +110,7 @@ public class MotionActorControl : Decorator
} }
_transformation = matrix; _transformation = matrix;
_matrixTransform.Matrix = matrix; _matrixTransform.Matrix = FilterScaleTransform(matrix);
// New transform means re-layout is necessary // New transform means re-layout is necessary
InvalidateMeasure(); InvalidateMeasure();
} }
@ -130,6 +132,17 @@ public class MotionActorControl : Decorator
matrix.M32); matrix.M32);
} }
private static Matrix FilterScaleTransform(Matrix matrix)
{
return new Matrix(
1.0,
matrix.M12,
matrix.M21,
1.0,
matrix.M31,
matrix.M32);
}
protected override Size ArrangeOverride(Size finalSize) protected override Size ArrangeOverride(Size finalSize)
{ {
if (MotionTransformRoot == null || MotionTransform == null) if (MotionTransformRoot == null || MotionTransform == null)
@ -184,7 +197,6 @@ public class MotionActorControl : Decorator
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size availableSize)
{ {
Console.WriteLine(MotionTransform);
if (MotionTransformRoot == null || MotionTransform == null) if (MotionTransformRoot == null || MotionTransform == null)
{ {
return base.MeasureOverride(availableSize); return base.MeasureOverride(availableSize);

View File

@ -6,7 +6,7 @@ using Avalonia.Styling;
namespace AtomUI.Controls.Utils; namespace AtomUI.Controls.Utils;
public static partial class MotionFactory internal static partial class MotionFactory
{ {
public static MotionConfig BuildCollapseMotion(Direction direction, TimeSpan duration, Easing? easing = null, public static MotionConfig BuildCollapseMotion(Direction direction, TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None) FillMode fillMode = FillMode.None)

View File

@ -1,9 +1,10 @@
using Avalonia; using Avalonia;
using Avalonia.Animation; using Avalonia.Animation;
using Avalonia.Media.Transformation;
namespace AtomUI.Controls.Utils; namespace AtomUI.Controls.Utils;
public record MotionConfig internal record MotionConfig
{ {
public RelativePoint RenderTransformOrigin { get; } public RelativePoint RenderTransformOrigin { get; }
public IList<Animation> Animations { get; } public IList<Animation> Animations { get; }
@ -13,4 +14,36 @@ public record MotionConfig
RenderTransformOrigin = renderTransformOrigin; RenderTransformOrigin = renderTransformOrigin;
Animations = animations; Animations = animations;
} }
}
internal static partial class MotionFactory
{
static TransformOperations BuildScaleTransform(double scaleX, double scaleY)
{
var builder = new TransformOperations.Builder(1);
builder.AppendScale(scaleX, scaleY);
return builder.Build();
}
static TransformOperations BuildScaleTransform(double scale)
{
return BuildScaleTransform(scale, scale);
}
static TransformOperations BuildScaleXTransform(double scale)
{
return BuildScaleTransform(scale, 1.0);
}
static TransformOperations BuildScaleYTransform(double scale)
{
return BuildScaleTransform(1.0, scale);
}
static TransformOperations BuildTranslateTransform(double offsetX, double offsetY)
{
var builder = new TransformOperations.Builder(1);
builder.AppendTranslate(offsetX, offsetY);
return builder.Build();
}
} }

View File

@ -1,24 +1,26 @@
using Avalonia; using AtomUI.Controls.Primitives;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Threading; using Avalonia.Threading;
namespace AtomUI.Controls.Utils; namespace AtomUI.Controls.Utils;
public static class MotionInvoker internal static class MotionInvoker
{ {
public static void Invoke(Control target, public static void Invoke(MotionActorControl target,
MotionConfig motionConfig, MotionConfig motionConfig,
Action? aboutToStart = null, Action? aboutToStart = null,
Action? completedAction = null) Action? completedAction = null)
{ {
Dispatcher.UIThread.InvokeAsync(async () => Dispatcher.UIThread.InvokeAsync(async () =>
{ {
using var originRestore = new RenderTransformOriginRestore(target); using var originRestore = new RenderTransformOriginRestore(target);
target.RenderTransformOrigin = motionConfig.RenderTransformOrigin; target.RenderTransformOrigin = motionConfig.RenderTransformOrigin;
if (aboutToStart != null) if (aboutToStart != null)
{ {
aboutToStart(); aboutToStart();
} }
foreach (var animation in motionConfig.Animations) foreach (var animation in motionConfig.Animations)
{ {
await animation.RunAsync(target); await animation.RunAsync(target);
@ -42,7 +44,7 @@ internal class RenderTransformOriginRestore : IDisposable
_target = target; _target = target;
_origin = target.RenderTransformOrigin; _origin = target.RenderTransformOrigin;
} }
public void Dispose() public void Dispose()
{ {
_target.RenderTransformOrigin = _origin; _target.RenderTransformOrigin = _origin;

View File

@ -4,7 +4,7 @@ using Avalonia.Media.Transformation;
namespace AtomUI.Controls.Utils; namespace AtomUI.Controls.Utils;
public class MotionTransformOptionsAnimator : InterpolatingAnimator<TransformOperations> internal class MotionTransformOptionsAnimator : InterpolatingAnimator<TransformOperations>
{ {
public override TransformOperations Interpolate(double progress, TransformOperations oldValue, TransformOperations newValue) public override TransformOperations Interpolate(double progress, TransformOperations oldValue, TransformOperations newValue)
{ {

View File

@ -6,7 +6,7 @@ using Avalonia.Styling;
namespace AtomUI.Controls.Utils; namespace AtomUI.Controls.Utils;
public static partial class MotionFactory internal static partial class MotionFactory
{ {
public static MotionConfig BuildMoveDownInMotion(double offset, TimeSpan duration, Easing? easing = null, public static MotionConfig BuildMoveDownInMotion(double offset, TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None) FillMode fillMode = FillMode.None)

View File

@ -1,17 +1,19 @@
using Avalonia; using AtomUI.Controls.Primitives;
using Avalonia;
using Avalonia.Animation; using Avalonia.Animation;
using Avalonia.Animation.Easings; using Avalonia.Animation.Easings;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Transformation;
using Avalonia.Styling; using Avalonia.Styling;
namespace AtomUI.Controls.Utils; namespace AtomUI.Controls.Utils;
public static partial class MotionFactory internal static partial class MotionFactory
{ {
public static MotionConfig BuildSlideUpInMotion(TimeSpan duration, Easing? easing = null, public static MotionConfig BuildSlideUpInMotion(TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None) FillMode fillMode = FillMode.None)
{ {
easing ??= new QuinticEaseOut(); easing ??= new CubicEaseOut();
var animations = new List<Animation>(); var animations = new List<Animation>();
RelativePoint transformOrigin = default; RelativePoint transformOrigin = default;
var animation = new Animation var animation = new Animation
@ -32,36 +34,14 @@ public static partial class MotionFactory
Value = 0.0 Value = 0.0
}; };
startFrame.Setters.Add(opacitySetter); startFrame.Setters.Add(opacitySetter);
var scaleYSetter = new Setter var scaleYSetter = new Setter
{ {
Property = ScaleTransform.ScaleYProperty, Property = MotionActorControl.MotionTransformProperty,
Value = 0.1 Value = BuildScaleYTransform(0.01) // // 不知道为啥设置成 0.0, 子元素渲染不正常
}; };
startFrame.Setters.Add(scaleYSetter); startFrame.Setters.Add(scaleYSetter);
} }
animation.Children.Add(startFrame); animation.Children.Add(startFrame);
var middleFrame = new KeyFrame
{
Cue = new Cue(0.9)
};
{
var opacitySetter = new Setter
{
Property = Visual.OpacityProperty,
Value = 0.2
};
middleFrame.Setters.Add(opacitySetter);
var scaleYSetter = new Setter
{
Property = ScaleTransform.ScaleYProperty,
Value = 0.9
};
middleFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(middleFrame);
var endFrame = new KeyFrame var endFrame = new KeyFrame
{ {
@ -76,8 +56,8 @@ public static partial class MotionFactory
endFrame.Setters.Add(opacitySetter); endFrame.Setters.Add(opacitySetter);
var scaleYSetter = new Setter var scaleYSetter = new Setter
{ {
Property = ScaleTransform.ScaleYProperty, Property = MotionActorControl.MotionTransformProperty,
Value = 1.0 Value = BuildScaleYTransform(1.0)
}; };
endFrame.Setters.Add(scaleYSetter); endFrame.Setters.Add(scaleYSetter);
} }
@ -91,7 +71,7 @@ public static partial class MotionFactory
public static MotionConfig BuildSlideUpOutMotion(TimeSpan duration, Easing? easing = null, public static MotionConfig BuildSlideUpOutMotion(TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None) FillMode fillMode = FillMode.None)
{ {
easing ??= new QuinticEaseIn(); easing ??= new CubicEaseIn();
var animations = new List<Animation>(); var animations = new List<Animation>();
RelativePoint transformOrigin = default; RelativePoint transformOrigin = default;
var animation = new Animation var animation = new Animation
@ -115,33 +95,12 @@ public static partial class MotionFactory
var scaleYSetter = new Setter var scaleYSetter = new Setter
{ {
Property = ScaleTransform.ScaleYProperty, Property = MotionActorControl.MotionTransformProperty,
Value = 1.0 Value = BuildScaleYTransform(1.0)
}; };
startFrame.Setters.Add(scaleYSetter); startFrame.Setters.Add(scaleYSetter);
} }
animation.Children.Add(startFrame); animation.Children.Add(startFrame);
var middleFrame = new KeyFrame
{
Cue = new Cue(0.9)
};
{
var opacitySetter = new Setter
{
Property = Visual.OpacityProperty,
Value = 0.2
};
middleFrame.Setters.Add(opacitySetter);
var scaleYSetter = new Setter
{
Property = ScaleTransform.ScaleYProperty,
Value = 0.9
};
middleFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(middleFrame);
var endFrame = new KeyFrame var endFrame = new KeyFrame
{ {
@ -156,8 +115,8 @@ public static partial class MotionFactory
endFrame.Setters.Add(opacitySetter); endFrame.Setters.Add(opacitySetter);
var scaleYSetter = new Setter var scaleYSetter = new Setter
{ {
Property = ScaleTransform.ScaleYProperty, Property = MotionActorControl.MotionTransformProperty,
Value = 0.0 Value = BuildScaleYTransform(0.0)
}; };
endFrame.Setters.Add(scaleYSetter); endFrame.Setters.Add(scaleYSetter);
} }