Avalonia 11 preview was just released and it's packing new features and performance improvements.
As a performance geek, the feature I look the most into is the new "Composition" Renderer.
What's the new Composition Renderer?
The renderer is responsible for tracking, validating, and triggering the drawing of visuals, making it one of the most important components for affecting the performance and efficiency of an app.
The new renderer achieves a UWP-like composition visual tree with UI and render thread separation.
As a result, Avalonia apps are now more efficient, use less RAM and allow render-thread only animations.
Into the new animations
When a layout change occurs in an Avalonia app the renderer will re-arrange visuals to fit the new bounds.
The new Composition APIs allow Avalonia devs to create silky-smooth animations when these layout changes occur.
There are two kinds of animations, explicit and implicit - the first triggers manually from code-behind, and the later triggers as properties of a visual change.
The new composition APIs
Every Visual
in Avalonia now has a CompositionVisual
counterpart that holds all its compositing properties, like Size
, Offset
, Opacity
, etc.
Just like the old animations, you can animate any of these properties.
For instance, here is an example of an explicit sliding-in animation of a control -
// Get the new composition visual
CompositionVisual compositionVisual = ElementComposition.GetElementVisual(control);
Compositor compositor = compositionVisual.Compositor;
// "Offset" is a Vector3 property, so we create a Vector3KeyFrameAnimation
Vector3KeyFrameAnimation animation = compositor.CreateVector3KeyFrameAnimation();
// Change the offset of the visual slightly to the left when the animation beginning
animation.InsertKeyFrame(0f, compositionVisual.Offset with {X = compositionVisual.Offset.X - 20});
// Revert the offset to the original position (0,0,0) when the animation ends
animation.InsertKeyFrame(1f, compositionVisual.Offset);
animation.Duration = TimeSpan.FromMilliseconds(300);
// Start the new animation!
compositionVisual.StartAnimation("Offset", animation);
Here we animate the Offset
property, which is a 3-vector, e.g.(x,y,z)
. By explicitly starting an animation with an offset of twenty vertically we can make a sliding-in animation -
Implicit animations
Implicit animations are as you've guessed - the opposite of explicit animations, they trigger as property of visual changes.
These animations are especially useful as you only set them once and they will trigger efficiently every time a property changes.
Each CompositionVisual
has an ImplicitAnimations
property that contains a collection that maps between a property name, e.g. Offset
to an animation or a group of animations to trigger when it changes.
As a simple example, here is how to implicitly animate the control's position change -
CompositionVisual compositionVisual = ElementComposition.GetElementVisual(control);
Compositor compositor = compositionVisual.Compositor;
Vector3KeyFrameAnimation offsetAnimation = compositor.CreateVector3KeyFrameAnimation();
offsetAnimation.Target = "Offset";
// Using the "this.FinalValue" to indicate the last value of the Offset property
offsetAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue");
offsetAnimation.Duration = TimeSpan.FromMilliseconds(400);
// Create a new implicit animation collection and bind the offset animation
ImplicitAnimationCollection implicitAnimationCollection = compositor.CreateImplicitAnimationCollection();
implicitAnimationCollection["Offset"] = offsetAnimation;
compositionVisual.ImplicitAnimations = implicitAnimationCollection;
First, we create an Offset animation, and unlike previously when we explicitly set the Offset
value, here we use an expression - this.FinalValue
to use the value of Offset
at the last frame.
Then we wire up the animation to the visual using a new ImplicitAnimationCollection
that maps between "Offset"
and our new offsetAnimation
.
That will tell the compositor to start the offsetAnimation
when the Offset
property changes.
As a result, layout changes of visuals are animated -
How do I combine it with XAML?
To extend XAML you can use an AttachedProperty
, which is a property that is declared outside of a Control
, but can still affect it.
Let's say we want to create a nice zoom-in animation for all our Windows, we will start by creating a new "Extension" class for our attached property -
public class WindowAnimation : AvaloniaObject
{
public static readonly AttachedProperty<bool> EnableScaleShowAnimationProperty =
AvaloniaProperty.RegisterAttached<WindowBase, bool>("EnableShowScaleAnimation",
typeof(WindowAnimation));
static WindowAnimation()
{
EnableScaleShowAnimationProperty.Changed.AddClassHandler<WindowBase>(OnEnableScaleShowAnimationChanged);
}
private static void OnEnableScaleShowAnimationChanged(WindowBase windowBase,
AvaloniaPropertyChangedEventArgs eventArgs)
{
if (eventArgs.NewValue is true)
{
windowBase.Opened += OnOpened;
}
else
{
windowBase.Opened -= OnOpened;
}
}
private static void OnOpened(object sender, EventArgs e)
{
if (sender is not WindowBase windowBase || !GetEnableScaleShowAnimation(windowBase))
return;
// Here we explicitly animate the "Scale" property
// The implementation is the same as `Offset` at the beginning, but just with the Scale property
windowBase.StartWindowScaleAnimation();
}
public static bool GetEnableScaleShowAnimation(WindowBase element)
{
return element.GetValue(EnableScaleShowAnimationProperty);
}
public static void SetEnableScaleShowAnimation(WindowBase element, bool value)
{
element.SetValue(EnableScaleShowAnimationProperty, value);
}
}
Then we can easily apply it to any Window using a Style
in App.xaml
-
<Style Selector="Window">
<Setter Property="animations:WindowAnimation.EnableScaleShowAnimation" Value="True"/>
</Style>
And that's it!
Hope you found this post interesting :)
Top comments (0)