Using the new Composition Renderer in Avalonia 11
Tags: avaloniaui, csharp, xaml
Published: 2022-08-23 16:18:11

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 -
```c#
// 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 -


![Sliding-in animation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g8dayz2nb64aav3ga8ng.gif)

## 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 -
```c#
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 -

![Offset animations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gavr5hrvqmsrbul6a3mt.gif)

## 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 -
```c#
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` -
```xaml
<Style Selector="Window">
    <Setter Property="animations:WindowAnimation.EnableScaleShowAnimation" Value="True"/>
</Style>
```

![Window animation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5p62m8to6i0kpvaujjpc.gif)

And that's it!
Hope you found this post interesting :)