Якщо ви вже знайомі з анімаціями у 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 для складних сценаріїв.Transition (перехід) — це механізм, який автоматично анімує зміну властивості елемента. Ви визначаєте, яку властивість потрібно анімувати, тривалість анімації, функцію пом'якшення (easing), і Avalonia автоматично застосовує плавний перехід щоразу, коли ця властивість змінюється.
Наприклад, якщо ви встановите Opacity кнопки з 1.0 на 0.5, і визначите DoubleTransition для властивості Opacity, Avalonia автоматично створить плавну анімацію зміни прозорості. Вам не потрібно викликати Storyboard.Begin() чи писати код-behind — все відбувається декларативно.
Transitions визначаються через властивість Transitions будь-якого Control. Це колекція об'єктів типу ITransition, кожен з яких відповідає за анімацію однієї властивості.
Базовий синтаксис:
<Button Content="Наведи на мене">
<Button.Transitions>
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.3"/>
</Transitions>
</Button.Transitions>
</Button>
Тепер, коли Opacity кнопки зміниться (наприклад, через стиль або код), зміна буде анімована протягом 300 мілісекунд.
Розглянемо класичний сценарій — зміна прозорості кнопки при наведенні миші. У 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>
Що тут відбувається:
Opacity має анімуватися протягом 0.3 секунди з функцією CubicEaseInOut.:pointerover), Opacity змінюється на 0.7.Opacity змінилася, і застосовує визначений DoubleTransition — плавний перехід від 1.0 до 0.7.Коли користувач прибирає мишу, Opacity повертається до 1.0, і анімація відбувається у зворотному напрямку.
Avalonia надає кілька вбудованих типів переходів для різних типів властивостей:
Анімує властивості типу double — Opacity, Width, Height, FontSize, кути обертання тощо.
<DoubleTransition Property="Opacity" Duration="0:0:0.5" Easing="QuadraticEaseOut"/>
Анімує зміну кольорів у 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.
Анімує властивості типу Thickness — Margin, Padding, BorderThickness.
<ThicknessTransition Property="Margin" Duration="0:0:0.3"/>
Це корисно для створення ефектів "розсування" елементів або зміни відступів.
Анімує трансформації — 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.
:pointerover, :pressed, :disabled), аналог WPF Triggers.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 створює ефект "перельоту" — елемент трохи виходить за межі кінцевого значення, а потім повертається назад.
Одна з найбільших переваг Avalonia — це інтеграція анімацій зі стилями через pseudo-classes (псевдокласи). Це селектори стану елемента, які автоматично застосовуються при певних умовах.
:pointerover — курсор миші над елементом (аналог WPF IsMouseOver).: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>
Що відбувається:
:pointerover): Прозорість зменшується до 0.8, кнопка збільшується до 105%.:pressed): Прозорість зменшується до 0.6, кнопка зменшується до 95% (ефект "натискання").Opacity та RenderTransform) анімуються плавно.Це всього 15 рядків XAML. У WPF аналогічний ефект вимагав би ControlTemplate з VisualStateManager або кілька EventTrigger з Storyboard — близько 40-50 рядків коду.
:pointerover замість :mouseover — це працює як з мишею, так і з сенсорними екранами.Хоча Transitions чудово підходять для простих анімацій (зміна однієї властивості з A на B), іноді потрібні складніші сценарії — наприклад, анімація з кількома етапами, зміна кількох властивостей одночасно, або анімація, яка запускається програмно (не через зміну властивості).
Для таких випадків Avalonia надає KeyFrame Animations — механізм, схожий на WPF Storyboard, але з більш зручним API.
KeyFrame анімація складається з:
Cue).Базовий синтаксис:
<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>
Що тут відбувається:
Infinite означає нескінченне повторення.0% — початок, 50% — середина, 100% — кінець.Результат: 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>
Важливі моменти:
0.5, 0.5 (центр елемента), щоб обертання відбувалося навколо центру, а не лівого верхнього кута.Створимо анімацію, яка одночасно змінює розмір, прозорість та колір фону:
<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, який завершується після закінчення анімації. Це зручно для послідовних анімацій або анімацій, які залежать від бізнес-логіки.
Давайте порівняємо обсяг коду для однієї й тієї ж анімації — зміна прозорості кнопки при наведенні миші.
<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
<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
EventTrigger, BeginStoryboard, TargetProperty.Переваги WPF Storyboard
(Button.RenderTransform).(ScaleTransform.ScaleX)).ParallelTimeline, BeginTime, RepeatBehavior="Forever" з точним контролем.Якщо вам потрібно анімувати зміну однієї властивості (прозорість, колір, розмір), завжди використовуйте Transitions. Це простіше, коротше та зрозуміліше.
<!-- ✅ Добре: Transition для простої анімації -->
<Button.Transitions>
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.3"/>
</Transitions>
</Button.Transitions>
Якщо анімація має кілька етапів, змінює багато властивостей одночасно, або потребує програмного запуску — використовуйте 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>
Якщо ви визначили 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 для однієї властивості, але не обидва одночасно.
Занадто швидкі анімації (< 100 мс) можуть бути непомітними. Занадто повільні (> 500 мс) можуть дратувати користувача. Оптимальний діапазон для більшості UI-анімацій — 200-400 мс.
| Тип анімації | Рекомендована тривалість |
|---|---|
| Hover-ефекти | 200-300 мс |
| Модальні вікна (поява/зникнення) | 300-400 мс |
| Переходи між сторінками | 400-500 мс |
| Мікроанімації (іконки, індикатори) | 150-250 мс |
Якщо ви анімуєте 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 (з попередньої статті про WPF анімації) та портуємо її на Avalonia, щоб побачити різницю в обсязі коду.
У WPF ми створювали анімацію, яка висуває бічну панель з лівого краю екрану. Панель спочатку має Width="0", а при натисканні кнопки розширюється до Width="250".
<!-- 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: ~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% та покращить читабельність.Мета: Створити кнопку, яка при наведенні миші плавно змінює колір фону та збільшується на 10%.
Вимоги:
BrushTransition для анімації Background.TransformOperationsTransition для анімації RenderTransform.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%. При прибиранні миші — повертається до початкового стану.
Мета: Створити іконку завантаження (spinner), яка безперервно обертається на 360 градусів.
Вимоги:
KeyFrame Animation з двома кадрами: 0% (0 градусів) та 100% (360 градусів).IterationCount="Infinite").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>
Очікуваний результат: Іконка безперервно обертається по годинниковій стрілці.
Мета: Взяти WPF приклад з бічною панеллю (sidebar) та повністю портувати його на Avalonia з використанням Transitions.
Вимоги:
Grid з двома колонками: Auto (для sidebar) та * (для основного контенту).0 та фон LightGray.250 пікселів.0.DoubleTransition для анімації Width.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, ви використовуєте:
Cue (відсотки часу) замість BeginTime та Duration.:pointerover, :pressed, :focus автоматично застосовують стилі, які анімуються через Transitions.Ключові переваги Avalonia:
TargetProperty, TargetName, BeginStoryboard.Якщо ви портуєте WPF додаток на Avalonia, заміна Storyboard на Transitions — це перше, що варто зробити. Це одразу зменшить обсяг коду та покращить читабельність.
<Button.Transitions>.KeyFrame.:pointerover, :pressed, :focus). Аналог WPF Triggers.0.5, 0.5 означає центр елемента.Infinite означає нескінченне повторення.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 для відео та аудіо. Створення складних візуальних ефектів.