Avalonia 11 preview была только что выпущена, и она содержит новые функции и улучшения производительности.
Как специалист по производительности, я больше всего обращаю внимание на новый рендерер «Композиция».
Что такое новый рендерер композиции?
Рендерер отвечает за отслеживание, проверку и запуск отрисовки визуальных объектов, что делает его одним из самых важных компонентов, влияющих на производительность и эффективность приложения.
В новом рендерере реализовано композиционное визуальное дерево, подобное UWP, с разделением потоков UI и рендеринга.
В результате приложения Avalonia стали более эффективными, потребляют меньше оперативной памяти и позволяют использовать анимацию только в потоке рендеринга.
Знакомство с новыми анимациями
Когда в приложении Avalonia происходит изменение макета, рендерер перестраивает визуальные эффекты, чтобы они соответствовали новым границам.
Новые API Composition позволяют разработчикам Avalonia создавать плавные анимации при изменении компоновки.
Существует два вида анимации, явная и неявная — первая запускается вручную из code-behind, а вторая запускается как свойство визуального изменения.
Новые API композиции
Каждый Visual
в Avalonia теперь имеет аналог CompositionVisual
, который содержит все свойства композитинга, такие как Size
, Offset
, Opacity
и т.д.
Как и в старых анимациях, вы можете анимировать любое из этих свойств.
Например, вот пример явной анимации скольжения элемента управления —
// 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);
Здесь мы анимируем свойство Offset
, которое является 3-вектором, например, (x,y,z)
. Явно начав анимацию со смещением на двадцать по вертикали, мы можем сделать анимацию скольжения.
Неявные анимации
Неявные анимации, как вы уже догадались, являются противоположностью явных анимаций, они запускаются в зависимости от визуальных изменений.
Эти анимации особенно полезны, поскольку вы задаете их только один раз, и они будут срабатывать эффективно при каждом изменении свойства.
Каждая CompositionVisual
имеет свойство ImplicitAnimations
, которое содержит коллекцию, отображающую имя свойства, например, Offset
на анимацию или группу анимаций, срабатывающих при его изменении.
В качестве простого примера, вот как неявно анимировать изменение положения элемента управления —
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;
Сначала мы создаем анимацию смещения, и в отличие от предыдущих случаев, когда мы явно задавали значение Offset
, здесь мы используем выражение — this.FinalValue
, чтобы использовать значение Offset
в последнем кадре.
Затем мы соединяем анимацию с визуальным образом с помощью новой коллекции ImplicitAnimationCollection
, которая отображается между "Offset"
и нашей новой offsetAnimation
.
Это позволит композитору запустить offsetAnimation
при изменении свойства Offset
.
В результате изменения расположения визуальных объектов будут анимированы.
Как совместить это с XAML?
Для расширения XAML можно использовать AttachedProperty
, которое является свойством, объявленным вне Control
, но может влиять на него.
Допустим, мы хотим создать красивую анимацию увеличения для всех наших Windows, мы начнем с создания нового класса «Extension» для нашего вложенного свойства — …
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);
}
}
Затем мы можем легко применить его к любому окну, используя Style
в App.xaml
—
<Style Selector="Window">
<Setter Property="animations:WindowAnimation.EnableScaleShowAnimation" Value="True"/>
</Style>
Вот и все!
Надеюсь, этот пост показался вам интересным 🙂