From e5758fc0737e9984d2364fd3502b2fc1d5bb89a2 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 27 Jan 2022 10:48:54 +0100 Subject: [PATCH] Add minor performance improvements. Include rotate 3d transition for carousel control. --- .../ControlCatalog/Pages/CarouselPage.xaml | 1 + .../ControlCatalog/Pages/CarouselPage.xaml.cs | 3 + src/Avalonia.Visuals/Animation/PageSlide.cs | 6 +- .../Transitions/Rotate3DTransition.cs | 153 ++++++++++++++++++ .../Media/Rotate3DTransform.cs | 24 ++- 5 files changed, 176 insertions(+), 11 deletions(-) create mode 100644 src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs diff --git a/samples/ControlCatalog/Pages/CarouselPage.xaml b/samples/ControlCatalog/Pages/CarouselPage.xaml index 4a53c9026f..1c2d768966 100644 --- a/samples/ControlCatalog/Pages/CarouselPage.xaml +++ b/samples/ControlCatalog/Pages/CarouselPage.xaml @@ -29,6 +29,7 @@ None Slide Crossfade + 3D Rotation diff --git a/samples/ControlCatalog/Pages/CarouselPage.xaml.cs b/samples/ControlCatalog/Pages/CarouselPage.xaml.cs index 66180d4ccb..6b7707be13 100644 --- a/samples/ControlCatalog/Pages/CarouselPage.xaml.cs +++ b/samples/ControlCatalog/Pages/CarouselPage.xaml.cs @@ -45,6 +45,9 @@ namespace ControlCatalog.Pages case 2: _carousel.PageTransition = new CrossFade(TimeSpan.FromSeconds(0.25)); break; + case 3: + _carousel.PageTransition = new Rotate3DTransition(TimeSpan.FromSeconds(0.5), _orientation.SelectedIndex == 0 ? PageSlide.SlideAxis.Horizontal : PageSlide.SlideAxis.Vertical); + break; } } } diff --git a/src/Avalonia.Visuals/Animation/PageSlide.cs b/src/Avalonia.Visuals/Animation/PageSlide.cs index b5a6062593..6fa0b4fb9c 100644 --- a/src/Avalonia.Visuals/Animation/PageSlide.cs +++ b/src/Avalonia.Visuals/Animation/PageSlide.cs @@ -10,7 +10,7 @@ using Avalonia.VisualTree; namespace Avalonia.Animation { /// - /// Transitions between two pages by sliding them horizontally. + /// Transitions between two pages by sliding them horizontally or vertically. /// public class PageSlide : IPageTransition { @@ -62,7 +62,7 @@ namespace Avalonia.Animation public Easing SlideOutEasing { get; set; } = new LinearEasing(); /// - public async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken) + public virtual async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -155,7 +155,7 @@ namespace Avalonia.Animation /// /// Any one of the parameters may be null, but not both. /// - private static IVisual GetVisualParent(IVisual? from, IVisual? to) + protected static IVisual GetVisualParent(IVisual? from, IVisual? to) { var p1 = (from ?? to)!.VisualParent; var p2 = (to ?? from)!.VisualParent; diff --git a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs new file mode 100644 index 0000000000..b603fe5f7b --- /dev/null +++ b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Avalonia.Media; +using Avalonia.Styling; + +namespace Avalonia.Animation; + +public class Rotate3DTransition: PageSlide +{ + + /// + /// Creates a new instance if the + /// + /// How long the rotation should take place + /// The orientation of the rotation + public Rotate3DTransition(TimeSpan duration, SlideAxis orientation = SlideAxis.Horizontal) + : base(duration, orientation) + {} + + /// + /// Creates a new instance if the + /// + public Rotate3DTransition() { } + + /// + public override async Task Start(Visual? @from, Visual? to, bool forward, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + var tasks = new List(); + var parent = GetVisualParent(from, to); + var (rotateProperty, center) = Orientation switch + { + SlideAxis.Vertical => (Rotate3DTransform.AngleXProperty, parent.Bounds.Height), + SlideAxis.Horizontal => (Rotate3DTransform.AngleYProperty, parent.Bounds.Width), + _ => throw new ArgumentOutOfRangeException() + }; + + var depthSetter = new Setter {Property = Rotate3DTransform.DepthProperty, Value = center}; + var centerZSetter = new Setter {Property = Rotate3DTransform.CenterZProperty, Value = -center / 2}; + + if (from != null) + { + var animation = new Animation + { + Duration = Duration, + Children = + { + new KeyFrame + { + Setters = + { + new Setter { Property = rotateProperty, Value = 0d }, + new Setter { Property = Visual.ZIndexProperty, Value = 2 }, + centerZSetter, + depthSetter, + }, + Cue = new Cue(0d) + }, + new KeyFrame + { + Setters = + { + new Setter { Property = rotateProperty, Value = 45d * (forward ? -1 : 1) }, + new Setter { Property = Visual.ZIndexProperty, Value = 1 }, + centerZSetter, + depthSetter + }, + Cue = new Cue(0.5d) + }, + new KeyFrame + { + Setters = + { + new Setter { Property = rotateProperty, Value = 90d * (forward ? -1 : 1) }, + new Setter { Property = Visual.ZIndexProperty, Value = 1 }, + centerZSetter, + depthSetter + }, + Cue = new Cue(1d) + } + } + }; + + tasks.Add(animation.RunAsync(from, null, cancellationToken)); + } + + if (to != null) + { + to.IsVisible = true; + var animation = new Animation + { + Duration = Duration, + Children = + { + new KeyFrame + { + Setters = + { + new Setter { Property = rotateProperty, Value = 90d * (forward ? 1 : -1) }, + new Setter { Property = Visual.ZIndexProperty, Value = 1 }, + centerZSetter, + depthSetter + }, + Cue = new Cue(0d) + }, + new KeyFrame + { + Setters = + { + new Setter { Property = Visual.ZIndexProperty, Value = 1 }, + new Setter { Property = rotateProperty, Value = 45d * (forward ? 1 : -1) }, + centerZSetter, + depthSetter + }, + Cue = new Cue(0.5d) + }, + new KeyFrame + { + Setters = + { + new Setter { Property = rotateProperty, Value = 0d }, + new Setter { Property = Visual.ZIndexProperty, Value = 2 }, + centerZSetter, + depthSetter, + }, + Cue = new Cue(1d) + } + } + }; + + tasks.Add(animation.RunAsync(to, null, cancellationToken)); + } + + await Task.WhenAll(tasks); + + if (from != null && !cancellationToken.IsCancellationRequested) + { + from.IsVisible = false; + from.ZIndex = 1; + } + + if (to != null && !cancellationToken.IsCancellationRequested) + { + to.ZIndex = 2; + } + } +} diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs index 3b8cc57b45..0fea9d73a0 100644 --- a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -9,6 +9,8 @@ namespace Avalonia.Media; /// public class Rotate3DTransform : Transform { + private readonly bool _isInitializing; + /// /// Defines the property. /// @@ -76,12 +78,14 @@ public class Rotate3DTransform : Transform double centerY, double centerZ) : this() { + _isInitializing = true; AngleX = angleX; AngleY = angleY; AngleZ = angleZ; CenterX = centerX; CenterY = centerY; CenterZ = centerZ; + _isInitializing = false; } /// @@ -148,21 +152,22 @@ public class Rotate3DTransform : Transform } /// - /// Gets the transform's . + /// Gets the transform's . /// public override Matrix Value { get { var matrix44 = Matrix4x4.Identity; + var centerSum = CenterX + CenterY + CenterZ; + + if (centerSum != 0) matrix44 *= Matrix4x4.CreateTranslation(-(float)CenterX, -(float)CenterY, -(float)CenterZ); - matrix44 *= Matrix4x4.CreateTranslation(-(float)CenterX, -(float)CenterY, -(float)CenterZ); + if (AngleX != 0) matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(AngleX)); + if (AngleY != 0) matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(AngleY)); + if (AngleZ != 0) matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(AngleZ)); - matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(AngleX)); - matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(AngleY)); - matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(AngleZ)); - - matrix44 *= Matrix4x4.CreateTranslation((float)CenterX, (float)CenterY, (float)CenterZ); + if (centerSum != 0) matrix44 *= Matrix4x4.CreateTranslation((float)CenterX, (float)CenterY, (float)CenterZ); if (Depth != 0) { @@ -186,5 +191,8 @@ public class Rotate3DTransform : Transform } } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) => RaiseChanged(); + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + if (!_isInitializing) RaiseChanged(); + } }