Анімації в Avalonia
Анімації в Avalonia
Вступ
Якщо ви вже знайомі з анімаціями у WPF, то підхід Avalonia може здатися вам революційним. Там, де WPF вимагає створення Storyboard, визначення DoubleAnimation, налаштування TargetProperty та TargetName, Avalonia пропонує набагато простіший механізм — Transitions (переходи).
Філософія Avalonia полягає в тому, що анімації мають бути неявними (implicit) та декларативними. Замість того, щоб явно описувати, як саме змінюється властивість від значення A до значення B, ви просто вказуєте: "коли ця властивість зміниться, анімуй її протягом 300 мілісекунд". Це нагадує підхід CSS-transitions у веб-розробці, де зміна opacity автоматично анімується, якщо визначено transition: opacity 0.3s.
У цій статті ми розглянемо, як працюють Transitions в Avalonia, які типи переходів доступні, як використовувати KeyFrame анімації для складніших сценаріїв, і порівняємо обсяг коду з WPF. Ви побачите, що той самий ефект, який у WPF займає 15-20 рядків XAML, в Avalonia можна реалізувати в 3-5 рядків.
Storyboard у тому вигляді, як у WPF. Замість цього використовуються Transitions для простих анімацій та Animation з KeyFrame для складних сценаріїв.Transitions — неявні анімації властивостей
Концепція Transitions
Transition (перехід) — це механізм, який автоматично анімує зміну властивості елемента. Ви визначаєте, яку властивість потрібно анімувати, тривалість анімації, функцію пом'якшення (easing), і Avalonia автоматично застосовує плавний перехід щоразу, коли ця властивість змінюється.
Наприклад, якщо ви встановите Opacity кнопки з 1.0 на 0.5, і визначите DoubleTransition для властивості Opacity, Avalonia автоматично створить плавну анімацію зміни прозорості. Вам не потрібно викликати Storyboard.Begin() чи писати код-behind — все відбувається декларативно.
Синтаксис Transitions
Transitions визначаються через властивість Transitions будь-якого Control. Це колекція об'єктів типу ITransition, кожен з яких відповідає за анімацію однієї властивості.
Базовий синтаксис:
<Button Content="Наведи на мене">
<Button.Transitions>
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.3"/>
</Transitions>
</Button.Transitions>
</Button>
Тепер, коли Opacity кнопки зміниться (наприклад, через стиль або код), зміна буде анімована протягом 300 мілісекунд.
Приклад: Hover-ефект з Transition
Розглянемо класичний сценарій — зміна прозорості кнопки при наведенні миші. У WPF це вимагало б EventTrigger, Storyboard, DoubleAnimation з TargetProperty. В Avalonia це виглядає так:
<Button Content="Hover Me" Opacity="1.0">
<Button.Transitions>
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.3" Easing="CubicEaseInOut"/>
</Transitions>
</Button.Transitions>
<Button.Styles>
<Style Selector="Button:pointerover">
<Setter Property="Opacity" Value="0.7"/>
</Style>
</Button.Styles>
</Button>
Що тут відбувається:
- Transitions: Визначаємо, що властивість
Opacityмає анімуватися протягом 0.3 секунди з функцієюCubicEaseInOut. - Styles: Коли користувач наводить мишу (
:pointerover),Opacityзмінюється на0.7. - Автоматична анімація: Avalonia бачить, що
Opacityзмінилася, і застосовує визначенийDoubleTransition— плавний перехід від1.0до0.7.
Коли користувач прибирає мишу, Opacity повертається до 1.0, і анімація відбувається у зворотному напрямку.
Типи Transitions
Avalonia надає кілька вбудованих типів переходів для різних типів властивостей:
DoubleTransition
Анімує властивості типу double — Opacity, Width, Height, FontSize, кути обертання тощо.
<DoubleTransition Property="Opacity" Duration="0:0:0.5" Easing="QuadraticEaseOut"/>
BrushTransition
Анімує зміну кольорів у Brush (наприклад, Background, Foreground). Avalonia інтерполює кольори, створюючи плавний перехід між відтінками.
<BrushTransition Property="Background" Duration="0:0:0.4"/>
Приклад використання:
<Border Background="LightBlue" CornerRadius="8" Padding="20">
<Border.Transitions>
<Transitions>
<BrushTransition Property="Background" Duration="0:0:0.4"/>
</Transitions>
</Border.Transitions>
<Border.Styles>
<Style Selector="Border:pointerover">
<Setter Property="Background" Value="LightCoral"/>
</Style>
</Border.Styles>
<TextBlock Text="Наведи на мене"/>
</Border>
При наведенні фон плавно змінюється з LightBlue на LightCoral.
ThicknessTransition
Анімує властивості типу Thickness — Margin, Padding, BorderThickness.
<ThicknessTransition Property="Margin" Duration="0:0:0.3"/>
Це корисно для створення ефектів "розсування" елементів або зміни відступів.
TransformOperationsTransition
Анімує трансформації — RenderTransform. Це найпотужніший тип переходу, оскільки дозволяє анімувати обертання, масштабування, зсув одночасно.
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.5" Easing="BackEaseOut"/>
Приклад — масштабування кнопки при наведенні:
<Button Content="Збільш мене" RenderTransformOrigin="0.5, 0.5">
<Button.Transitions>
<Transitions>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.3"/>
</Transitions>
</Button.Transitions>
<Button.Styles>
<Style Selector="Button:pointerover">
<Setter Property="RenderTransform" Value="scale(1.1)"/>
</Style>
</Button.Styles>
</Button>
Тут використовується CSS-подібний синтаксис scale(1.1) для масштабування. Avalonia підтримує функції translate(), rotate(), scale(), skew() у властивості RenderTransform.
- Transition — механізм автоматичної анімації зміни властивості.
- Easing — функція пом'якшення, яка визначає темп анімації (лінійний, прискорення, уповільнення тощо).
- Pseudo-class — селектор стану елемента (
:pointerover,:pressed,:disabled), аналог WPF Triggers.
Easing Functions у Avalonia
Avalonia підтримує ті ж функції пом'якшення (easing), що й WPF, але з дещо іншими назвами. Ось найпоширеніші:
| Avalonia Easing | Опис | Аналог у WPF |
|---|---|---|
LinearEasing | Лінійна анімація без прискорення | LinearEase |
QuadraticEaseIn | Прискорення на початку | QuadraticEase (EaseIn) |
QuadraticEaseOut | Уповільнення в кінці | QuadraticEase (EaseOut) |
CubicEaseInOut | Прискорення на початку, уповільнення в кінці | CubicEase (EaseInOut) |
BackEaseOut | "Відскок" в кінці анімації | BackEase (EaseOut) |
BounceEaseOut | Ефект "стрибка" в кінці | BounceEase (EaseOut) |
ElasticEaseOut | Еластичний ефект (як пружина) | ElasticEase (EaseOut) |
Приклад використання:
<DoubleTransition Property="Opacity" Duration="0:0:0.5" Easing="BackEaseOut"/>
Функції easing роблять анімації більш природними та приємними для ока. Наприклад, BackEaseOut створює ефект "перельоту" — елемент трохи виходить за межі кінцевого значення, а потім повертається назад.
CSS-подібний підхід: Pseudo-classes та Styles
Одна з найбільших переваг Avalonia — це інтеграція анімацій зі стилями через pseudo-classes (псевдокласи). Це селектори стану елемента, які автоматично застосовуються при певних умовах.
Основні Pseudo-classes
:pointerover— курсор миші над елементом (аналог WPFIsMouseOver).:pressed— елемент натиснутий (аналогIsPressed).:disabled— елемент вимкнений (аналогIsEnabled=False).:focus— елемент має фокус клавіатури.:checked— дляCheckBoxтаRadioButton(аналогIsChecked=True).
Приклад: Анімація кнопки з кількома станами
<Button Content="Інтерактивна кнопка" Opacity="1.0" RenderTransformOrigin="0.5, 0.5">
<Button.Transitions>
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.2"/>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.2"/>
</Transitions>
</Button.Transitions>
<Button.Styles>
<Style Selector="Button:pointerover">
<Setter Property="Opacity" Value="0.8"/>
<Setter Property="RenderTransform" Value="scale(1.05)"/>
</Style>
<Style Selector="Button:pressed">
<Setter Property="Opacity" Value="0.6"/>
<Setter Property="RenderTransform" Value="scale(0.95)"/>
</Style>
</Button.Styles>
</Button>
Що відбувається:
- Hover (
:pointerover): Прозорість зменшується до0.8, кнопка збільшується до105%. - Pressed (
:pressed): Прозорість зменшується до0.6, кнопка зменшується до95%(ефект "натискання"). - Transitions: Обидві властивості (
OpacityтаRenderTransform) анімуються плавно.
Це всього 15 рядків XAML. У WPF аналогічний ефект вимагав би ControlTemplate з VisualStateManager або кілька EventTrigger з Storyboard — близько 40-50 рядків коду.
:pointerover замість :mouseover — це працює як з мишею, так і з сенсорними екранами.KeyFrame Animations — складні сценарії
Хоча Transitions чудово підходять для простих анімацій (зміна однієї властивості з A на B), іноді потрібні складніші сценарії — наприклад, анімація з кількома етапами, зміна кількох властивостей одночасно, або анімація, яка запускається програмно (не через зміну властивості).
Для таких випадків Avalonia надає KeyFrame Animations — механізм, схожий на WPF Storyboard, але з більш зручним API.
Структура KeyFrame Animation
KeyFrame анімація складається з:
- Animation — контейнер, який визначає тривалість, режим повторення, затримку.
- KeyFrame — ключовий кадр, який визначає стан властивостей у певний момент часу (через
Cue). - Setter — встановлення значення властивості у цьому кадрі.
Базовий синтаксис:
<Animation Duration="0:0:2" IterationCount="Infinite">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="1.0"/>
</KeyFrame>
<KeyFrame Cue="50%">
<Setter Property="Opacity" Value="0.3"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="1.0"/>
</KeyFrame>
</Animation>
Що тут відбувається:
- Duration: Загальна тривалість анімації — 2 секунди.
- IterationCount: Кількість повторень.
Infiniteозначає нескінченне повторення. - Cue: Відсоток часу анімації.
0%— початок,50%— середина,100%— кінець. - Setter: Встановлює значення властивості у цьому кадрі.
Результат: Opacity плавно змінюється від 1.0 до 0.3 (перша половина анімації), потім від 0.3 до 1.0 (друга половина). Анімація повторюється нескінченно — ефект "пульсації".
Приклад: Обертання елемента
Створимо анімацію обертання іконки на 360 градусів:
<Border Width="100" Height="100" Background="DodgerBlue" CornerRadius="50"
RenderTransformOrigin="0.5, 0.5">
<Border.Styles>
<Style Selector="Border">
<Style.Animations>
<Animation Duration="0:0:2" IterationCount="Infinite">
<KeyFrame Cue="0%">
<Setter Property="RenderTransform" Value="rotate(0deg)"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="RenderTransform" Value="rotate(360deg)"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</Border.Styles>
<TextBlock Text="🔄" FontSize="48" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
Важливі моменти:
- Style.Animations: KeyFrame анімації визначаються всередині стилю. Це означає, що анімація застосовується автоматично при завантаженні елемента.
- RenderTransformOrigin: Встановлено на
0.5, 0.5(центр елемента), щоб обертання відбувалося навколо центру, а не лівого верхнього кута. - rotate(0deg) → rotate(360deg): CSS-подібний синтаксис для обертання.
Приклад: Складна анімація з кількома властивостями
Створимо анімацію, яка одночасно змінює розмір, прозорість та колір фону:
<Border Width="100" Height="100" Background="LightGreen" CornerRadius="8"
RenderTransformOrigin="0.5, 0.5" Opacity="1.0">
<Border.Styles>
<Style Selector="Border">
<Style.Animations>
<Animation Duration="0:0:3" IterationCount="Infinite">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="1.0"/>
<Setter Property="RenderTransform" Value="scale(1.0)"/>
<Setter Property="Background" Value="LightGreen"/>
</KeyFrame>
<KeyFrame Cue="50%">
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="RenderTransform" Value="scale(1.3)"/>
<Setter Property="Background" Value="LightCoral"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="1.0"/>
<Setter Property="RenderTransform" Value="scale(1.0)"/>
<Setter Property="Background" Value="LightGreen"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</Border.Styles>
<TextBlock Text="✨" FontSize="36" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
Ця анімація створює ефект "дихання" — елемент збільшується, стає напівпрозорим та змінює колір, потім повертається до початкового стану.
TargetName або TargetProperty у форматі (Button.Opacity), як у WPF. Просто вказуєте Property="Opacity" — набагато простіше!Програмне керування анімаціями
На відміну від Transitions, які запускаються автоматично, KeyFrame анімації можна запускати програмно через код:
// У ViewModel або Code-Behind
var animation = new Animation
{
Duration = TimeSpan.FromSeconds(1),
Children =
{
new KeyFrame
{
Cue = new Cue(0),
Setters = { new Setter(OpacityProperty, 0.0) }
},
new KeyFrame
{
Cue = new Cue(1),
Setters = { new Setter(OpacityProperty, 1.0) }
}
}
};
await animation.RunAsync(myControl);
Метод RunAsync() запускає анімацію та повертає Task, який завершується після закінчення анімації. Це зручно для послідовних анімацій або анімацій, які залежать від бізнес-логіки.
Порівняння WPF Storyboard vs Avalonia Transitions
Давайте порівняємо обсяг коду для однієї й тієї ж анімації — зміна прозорості кнопки при наведенні миші.
WPF Storyboard (verbose, explicit)
<Button Content="Hover Me">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To="0.7"
Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseInOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To="1.0"
Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseInOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Рядків коду: ~25
Avalonia Transitions (concise, implicit)
<Button Content="Hover Me" Opacity="1.0">
<Button.Transitions>
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.3" Easing="CubicEaseInOut"/>
</Transitions>
</Button.Transitions>
<Button.Styles>
<Style Selector="Button:pointerover">
<Setter Property="Opacity" Value="0.7"/>
</Style>
</Button.Styles>
</Button>
Рядків коду: ~10
Порівняльна таблиця
| Аспект | WPF Storyboard | Avalonia Transitions |
|---|---|---|
| Обсяг коду | 25+ рядків | 10 рядків |
| Читабельність | Складна вкладеність | Плоска структура |
| Двосторонність | Потрібні 2 EventTrigger (Enter/Leave) | Автоматично працює в обидва боки |
| Easing | Окремий елемент <EasingFunction> | Атрибут Easing="..." |
| TargetProperty | Рядок "Opacity" з можливістю помилки | Строго типізована властивість |
| Запуск | Явний через BeginStoryboard | Неявний при зміні властивості |
Переваги Avalonia Transitions
- Менше коду: У 2-3 рази менше XAML для тієї ж анімації.
- Простіше: Не потрібно розуміти
EventTrigger,BeginStoryboard,TargetProperty. - Декларативність: Описуєте "що" анімувати, а не "як" це робити.
- CSS-подібність: Знайомий підхід для веб-розробників.
- Автоматична двосторонність: Не потрібно писати окремі анімації для "вперед" та "назад".
Переваги WPF Storyboard
- Більше контролю: Можна анімувати вкладені властивості (наприклад,
(Button.RenderTransform).(ScaleTransform.ScaleX)). - Складні сценарії:
ParallelTimeline,BeginTime,RepeatBehavior="Forever"з точним контролем. - Зрілість: Більше документації, прикладів, Stack Overflow відповідей.
Практичні поради та Best Practices
1. Використовуйте Transitions для простих анімацій
Якщо вам потрібно анімувати зміну однієї властивості (прозорість, колір, розмір), завжди використовуйте Transitions. Це простіше, коротше та зрозуміліше.
<!-- ✅ Добре: Transition для простої анімації -->
<Button.Transitions>
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.3"/>
</Transitions>
</Button.Transitions>
2. Використовуйте KeyFrame для складних сценаріїв
Якщо анімація має кілька етапів, змінює багато властивостей одночасно, або потребує програмного запуску — використовуйте KeyFrame Animations.
<!-- ✅ Добре: KeyFrame для складної анімації -->
<Style.Animations>
<Animation Duration="0:0:2">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="0"/>
<Setter Property="RenderTransform" Value="translateY(-20px)"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="1"/>
<Setter Property="RenderTransform" Value="translateY(0)"/>
</KeyFrame>
</Animation>
</Style.Animations>
3. Не змішуйте Transitions та KeyFrame для однієї властивості
Якщо ви визначили DoubleTransition для Opacity, і одночасно маєте KeyFrame Animation, яка також змінює Opacity, результат може бути непередбачуваним. Avalonia спробує застосувати обидві анімації, що призведе до конфліктів.
<!-- ❌ Погано: Конфлікт між Transition та KeyFrame -->
<Button.Transitions>
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.5"/>
</Transitions>
</Button.Transitions>
<Button.Styles>
<Style Selector="Button">
<Style.Animations>
<Animation Duration="0:0:1">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="0"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="1"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</Button.Styles>
Рішення: Використовуйте або Transition, або KeyFrame Animation для однієї властивості, але не обидва одночасно.
4. Оптимізуйте тривалість анімацій
Занадто швидкі анімації (< 100 мс) можуть бути непомітними. Занадто повільні (> 500 мс) можуть дратувати користувача. Оптимальний діапазон для більшості UI-анімацій — 200-400 мс.
| Тип анімації | Рекомендована тривалість |
|---|---|
| Hover-ефекти | 200-300 мс |
| Модальні вікна (поява/зникнення) | 300-400 мс |
| Переходи між сторінками | 400-500 мс |
| Мікроанімації (іконки, індикатори) | 150-250 мс |
5. Використовуйте RenderTransformOrigin для обертання та масштабування
Якщо ви анімуєте RenderTransform (обертання, масштабування), завжди встановлюйте RenderTransformOrigin, щоб контролювати точку трансформації.
<!-- ✅ Добре: Обертання навколо центру -->
<Button RenderTransformOrigin="0.5, 0.5">
<Button.Styles>
<Style Selector="Button:pointerover">
<Setter Property="RenderTransform" Value="rotate(5deg)"/>
</Style>
</Button.Styles>
</Button>
Без RenderTransformOrigin обертання відбуватиметься навколо лівого верхнього кута (0, 0), що виглядає неприродно.
RenderTransformOrigin за замовчуванням 0, 0, в Avalonia це також 0, 0. Завжди явно встановлюйте 0.5, 0.5 для центрованих трансформацій.Реальний приклад: Портування WPF анімації на Avalonia
Давайте візьмемо складну анімацію з WPF (з попередньої статті про WPF анімації) та портуємо її на Avalonia, щоб побачити різницю в обсязі коду.
Сценарій: Анімація бічної панелі (Sidebar)
У WPF ми створювали анімацію, яка висуває бічну панель з лівого краю екрану. Панель спочатку має Width="0", а при натисканні кнопки розширюється до Width="250".
WPF версія (Storyboard)
<!-- WPF: ~30 рядків коду -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="Sidebar" Grid.Column="0" Width="0" Background="LightGray">
<StackPanel Margin="10">
<TextBlock Text="Меню" FontWeight="Bold"/>
<Button Content="Пункт 1" Margin="0,5"/>
<Button Content="Пункт 2" Margin="0,5"/>
</StackPanel>
</Border>
<Button Grid.Column="1" Content="Показати меню" Click="ToggleSidebar_Click"/>
</Grid>
<!-- Code-Behind -->
<Window.Resources>
<Storyboard x:Key="ShowSidebar">
<DoubleAnimation Storyboard.TargetName="Sidebar"
Storyboard.TargetProperty="Width"
To="250"
Duration="0:0:0.4">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
<Storyboard x:Key="HideSidebar">
<DoubleAnimation Storyboard.TargetName="Sidebar"
Storyboard.TargetProperty="Width"
To="0"
Duration="0:0:0.4">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</Window.Resources>
// Code-Behind: ~10 рядків
private bool isSidebarVisible = false;
private void ToggleSidebar_Click(object sender, RoutedEventArgs e)
{
var storyboard = isSidebarVisible
? (Storyboard)FindResource("HideSidebar")
: (Storyboard)FindResource("ShowSidebar");
storyboard.Begin();
isSidebarVisible = !isSidebarVisible;
}
Загалом: ~40 рядків XAML + Code-Behind.
Avalonia версія (Transitions)
<!-- Avalonia: ~15 рядків коду -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="Sidebar" Grid.Column="0" Width="0" Background="LightGray">
<Border.Transitions>
<Transitions>
<DoubleTransition Property="Width" Duration="0:0:0.4" Easing="CubicEaseOut"/>
</Transitions>
</Border.Transitions>
<StackPanel Margin="10">
<TextBlock Text="Меню" FontWeight="Bold"/>
<Button Content="Пункт 1" Margin="0,5"/>
<Button Content="Пункт 2" Margin="0,5"/>
</StackPanel>
</Border>
<Button Grid.Column="1" Content="Показати меню" Click="ToggleSidebar_Click"/>
</Grid>
// Code-Behind: ~5 рядків
private bool isSidebarVisible = false;
private void ToggleSidebar_Click(object sender, RoutedEventArgs e)
{
Sidebar.Width = isSidebarVisible ? 0 : 250;
isSidebarVisible = !isSidebarVisible;
}
Загалом: ~20 рядків XAML + Code-Behind.
Порівняння
| Аспект | WPF | Avalonia | Різниця |
|---|---|---|---|
| Рядків XAML | 30 | 15 | -50% |
| Рядків C# | 10 | 5 | -50% |
| Storyboard ресурси | 2 (Show/Hide) | 0 | -100% |
| FindResource() | Так | Ні | Простіше |
| Читабельність | Середня | Висока | Зрозуміліше |
Avalonia версія вдвічі коротша та набагато зрозуміліша. Не потрібно створювати окремі Storyboard для "показати" та "сховати" — Transition автоматично працює в обидва боки.
Storyboard на Transitions. Це одразу зменшить обсяг коду на 30-50% та покращить читабельність.Практичні завдання
Рівень 1: Hover-анімація кнопки через Transition
Мета: Створити кнопку, яка при наведенні миші плавно змінює колір фону та збільшується на 10%.
Вимоги:
- Використайте
BrushTransitionдля анімаціїBackground. - Використайте
TransformOperationsTransitionдля анімаціїRenderTransform. - Тривалість анімації — 300 мс.
- Easing функція —
CubicEaseInOut.
Підказка:
<Button Content="Наведи на мене" Background="DodgerBlue" RenderTransformOrigin="0.5, 0.5">
<Button.Transitions>
<Transitions>
<!-- Додайте BrushTransition та TransformOperationsTransition -->
</Transitions>
</Button.Transitions>
<Button.Styles>
<Style Selector="Button:pointerover">
<!-- Встановіть Background="LightCoral" та RenderTransform="scale(1.1)" -->
</Style>
</Button.Styles>
</Button>
Очікуваний результат: При наведенні миші кнопка плавно змінює колір з синього на коралловий та збільшується на 10%. При прибиранні миші — повертається до початкового стану.
Рівень 2: KeyFrame анімація обертання
Мета: Створити іконку завантаження (spinner), яка безперервно обертається на 360 градусів.
Вимоги:
- Використайте
KeyFrame Animationз двома кадрами:0%(0 градусів) та100%(360 градусів). - Анімація має повторюватися нескінченно (
IterationCount="Infinite"). - Тривалість одного обертання — 1.5 секунди.
- Використайте
LinearEasingдля рівномірного обертання.
Підказка:
<Border Width="50" Height="50" Background="Transparent" RenderTransformOrigin="0.5, 0.5">
<Border.Styles>
<Style Selector="Border">
<Style.Animations>
<Animation Duration="0:0:1.5" IterationCount="Infinite">
<KeyFrame Cue="0%">
<Setter Property="RenderTransform" Value="rotate(0deg)"/>
</KeyFrame>
<KeyFrame Cue="100%">
<!-- Додайте rotate(360deg) -->
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</Border.Styles>
<Path Data="M 25,5 A 20,20 0 1,1 24.9,5" Stroke="DodgerBlue" StrokeThickness="4"/>
</Border>
Очікуваний результат: Іконка безперервно обертається по годинниковій стрілці.
Рівень 3: Портувати sidebar-анімацію з WPF на Avalonia Transitions
Мета: Взяти WPF приклад з бічною панеллю (sidebar) та повністю портувати його на Avalonia з використанням Transitions.
Вимоги:
- Створіть
Gridз двома колонками:Auto(для sidebar) та*(для основного контенту). - Sidebar має початкову ширину
0та фонLightGray. - При натисканні кнопки "Показати меню" sidebar розширюється до
250пікселів. - При повторному натисканні — згортається назад до
0. - Використайте
DoubleTransitionдля анімаціїWidth. - Тривалість анімації — 400 мс, easing —
CubicEaseOut.
Підказка:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="Sidebar" Grid.Column="0" Width="0" Background="LightGray">
<Border.Transitions>
<!-- Додайте DoubleTransition для Width -->
</Border.Transitions>
<StackPanel Margin="10">
<TextBlock Text="Меню" FontWeight="Bold"/>
<Button Content="Пункт 1" Margin="0,5"/>
<Button Content="Пункт 2" Margin="0,5"/>
<Button Content="Пункт 3" Margin="0,5"/>
</StackPanel>
</Border>
<StackPanel Grid.Column="1" Margin="20">
<Button Content="Показати меню" Click="ToggleSidebar_Click"/>
<TextBlock Text="Основний контент" Margin="0,20"/>
</StackPanel>
</Grid>
// Code-Behind
private bool isSidebarVisible = false;
private void ToggleSidebar_Click(object sender, RoutedEventArgs e)
{
// Додайте логіку перемикання Width між 0 та 250
}
Очікуваний результат: При натисканні кнопки sidebar плавно висувається з лівого краю. При повторному натисканні — плавно ховається.
Бонус: Додайте анімацію Opacity для sidebar (від 0 до 1), щоб панель не тільки висувалася, але й плавно з'являлася.
Резюме
Avalonia пропонує революційно простіший підхід до анімацій порівняно з WPF. Замість складних Storyboard з EventTrigger та BeginStoryboard, ви використовуєте:
- Transitions — для простих анімацій властивостей. Визначаєте, яку властивість анімувати, тривалість та easing — все інше відбувається автоматично.
- KeyFrame Animations — для складних сценаріїв з кількома етапами. CSS-подібний синтаксис з
Cue(відсотки часу) замістьBeginTimeтаDuration. - Pseudo-classes — для інтеграції анімацій зі стилями.
:pointerover,:pressed,:focusавтоматично застосовують стилі, які анімуються черезTransitions.
Ключові переваги Avalonia:
- Менше коду: У 2-3 рази менше XAML для тієї ж анімації.
- Простіше: Не потрібно розуміти
TargetProperty,TargetName,BeginStoryboard. - Декларативність: Описуєте "що" анімувати, а не "як".
- Автоматична двосторонність: Transitions працюють в обидва боки без додаткового коду.
Якщо ви портуєте WPF додаток на Avalonia, заміна Storyboard на Transitions — це перше, що варто зробити. Це одразу зменшить обсяг коду та покращить читабельність.
Словник термінів
- Transition — механізм автоматичної анімації зміни властивості. Визначається через
<Button.Transitions>. - KeyFrame Animation — анімація з кількома ключовими кадрами, кожен з яких визначає стан властивостей у певний момент часу.
- Cue — відсоток часу анімації (0% — початок, 100% — кінець). Використовується у
KeyFrame. - Pseudo-class — селектор стану елемента (
:pointerover,:pressed,:focus). Аналог WPF Triggers. - Easing Function — функція пом'якшення, яка визначає темп анімації (лінійний, прискорення, уповільнення, відскок тощо).
- RenderTransformOrigin — точка, навколо якої відбувається трансформація (обертання, масштабування). Значення
0.5, 0.5означає центр елемента. - IterationCount — кількість повторень анімації.
Infiniteозначає нескінченне повторення. - TransformOperationsTransition — тип переходу для анімації трансформацій (
RenderTransform).
Додаткові ресурси
Avalonia Animations Documentation
Avalonia Animations Tutorial
Avalonia Samples — Animations
Easing Functions Visualizer
Наступна стаття: 2D/3D графіка та мультимедіа — використання графічних примітивів, Path, Brushes, Geometries та MediaElement у WPF.
Анімації у WPF: Storyboard та Easing Functions
Створення плавних анімацій для інтерактивних інтерфейсів. Storyboard, DoubleAnimation, ColorAnimation, Easing Functions, Event Triggers та code-behind анімації.
2D Графіка та Мультимедіа у WPF
Векторна графіка через Shapes та Path. Градієнти, геометрії, трансформації. MediaElement для відео та аудіо. Створення складних візуальних ефектів.