Avalonia provides a powerful animation system that allows you to create smooth, performant animations for any property. The animation system is built on a flexible architecture that supports keyframe animations, property transitions, and custom animators.
Animation Architecture
The animation system consists of several key components:
- Animatable: Base class for all objects that can be animated
- Animation: Defines keyframe-based animations
- Transitions: Automatically animate property changes
- Animators: Execute the interpolation between values
- Easing Functions: Control the rate of change over time
Keyframe Animations
Keyframe animations allow you to define specific values at specific points in time. The animation system interpolates between these keyframes.
Basic Animation
<Border>
<Border.Styles>
<Style Selector="Border">
<Style.Animations>
<Animation Duration="0:0:2" IterationCount="Infinite">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="0.0"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="1.0"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</Border.Styles>
</Border>
Animation Properties
From ~/workspace/source/src/Avalonia.Base/Animation/Animation.cs:
| Property | Type | Description |
|---|
Duration | TimeSpan | The active time of the animation |
IterationCount | IterationCount | Number of times to repeat (supports Infinite) |
PlaybackDirection | PlaybackDirection | Normal, Reverse, Alternate, AlternateReverse |
FillMode | FillMode | How values are applied before/after animation |
Easing | Easing | Easing function (default: LinearEasing) |
Delay | TimeSpan | Initial delay before animation starts |
DelayBetweenIterations | TimeSpan | Delay between iteration repeats |
SpeedRatio | double | Speed multiplier (default: 1.0) |
KeyFrame Timing
Keyframes can be specified using either cues (percentage) or absolute time:
<!-- Using Cue (percentage-based) -->
<KeyFrame Cue="0%">
<Setter Property="Width" Value="100"/>
</KeyFrame>
<KeyFrame Cue="50%">
<Setter Property="Width" Value="200"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Width" Value="100"/>
</KeyFrame>
<!-- Using KeyTime (absolute time) -->
<Animation Duration="0:0:4">
<KeyFrame KeyTime="0:0:0">
<Setter Property="Height" Value="50"/>
</KeyFrame>
<KeyFrame KeyTime="0:0:2">
<Setter Property="Height" Value="150"/>
</KeyFrame>
</Animation>
You cannot mix Cue and KeyTime in the same KeyFrame. Choose one timing mode and use it consistently.
KeySpline for Custom Timing
Use KeySpline to define cubic bezier curves for per-keyframe easing:
<KeyFrame Cue="50%" KeySpline="0.25,0.1,0.25,1.0">
<Setter Property="Opacity" Value="0.5"/>
</KeyFrame>
Property Transitions
Transitions automatically animate property changes. They’re simpler than keyframe animations but very effective for interactive UI.
Defining Transitions
<Border>
<Border.Transitions>
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.3"/>
<ThicknessTransition Property="Margin" Duration="0:0:0.2"/>
<BrushTransition Property="Background" Duration="0:0:0.4"/>
</Transitions>
</Border.Transitions>
</Border>
Available Transition Types
From ~/workspace/source/src/Avalonia.Base/Animation/Transitions/:
DoubleTransition - For double properties
FloatTransition - For float properties
IntegerTransition - For int properties
BoolTransition - For bool properties
ColorTransition - For Color properties
BrushTransition - For IBrush properties
PointTransition - For Point properties
SizeTransition - For Size properties
ThicknessTransition - For Thickness properties
CornerRadiusTransition - For CornerRadius properties
VectorTransition - For Vector properties
BoxShadowsTransition - For BoxShadows properties
TransformOperationsTransition - For transform operations
Rotate3DTransition - For 3D rotations
Transition with Easing
public class MyControl : UserControl
{
public MyControl()
{
Transitions = new Transitions
{
new DoubleTransition
{
Property = OpacityProperty,
Duration = TimeSpan.FromSeconds(0.3),
Easing = new CubicEaseOut()
}
};
}
}
Easing Functions
Easing functions control the rate of change during an animation. Avalonia includes 30+ built-in easing functions.
Built-in Easings
From ~/workspace/source/src/Avalonia.Base/Animation/Easings/:
Linear
LinearEasing - Constant speed
Quadratic
QuadraticEaseIn, QuadraticEaseOut, QuadraticEaseInOut
Cubic
CubicEaseIn, CubicEaseOut, CubicEaseInOut
Quartic
QuarticEaseIn, QuarticEaseOut, QuarticEaseInOut
Quintic
QuinticEaseIn, QuinticEaseOut, QuinticEaseInOut
Sine
SineEaseIn, SineEaseOut, SineEaseInOut
Exponential
ExponentialEaseIn, ExponentialEaseOut, ExponentialEaseInOut
Circular
CircularEaseIn, CircularEaseOut, CircularEaseInOut
Back
BackEaseIn, BackEaseOut, BackEaseInOut
Elastic
ElasticEaseIn, ElasticEaseOut, ElasticEaseInOut
Bounce
BounceEaseIn, BounceEaseOut, BounceEaseInOut
Advanced
SplineEasing - Custom cubic bezier curve
SpringEasing - Physics-based spring animation
Using Easings in XAML
<DoubleTransition Property="Width" Duration="0:0:0.5">
<DoubleTransition.Easing>
<CubicEaseOut/>
</DoubleTransition.Easing>
</DoubleTransition>
Custom Spline Easing
<DoubleTransition Property="Height" Duration="0:0:0.4">
<DoubleTransition.Easing>
<SplineEasing ControlPointX1="0.42" ControlPointY1="0.0"
ControlPointX2="0.58" ControlPointY2="1.0"/>
</DoubleTransition.Easing>
</DoubleTransition>
Spring Easing
<DoubleTransition Property="ScaleX" Duration="0:0:1">
<DoubleTransition.Easing>
<SpringEasing Mass="1" Stiffness="100" Damping="10"/>
</DoubleTransition.Easing>
</DoubleTransition>
Programmatic Animation
Running Animations from Code
using Avalonia.Animation;
using Avalonia.Animation.Easings;
public async Task AnimateButton()
{
var animation = new Animation
{
Duration = TimeSpan.FromSeconds(1),
Easing = new CubicEaseInOut(),
Children =
{
new KeyFrame
{
Cue = new Cue(0),
Setters =
{
new Setter(OpacityProperty, 1.0),
new Setter(ScaleTransform.ScaleXProperty, 1.0)
}
},
new KeyFrame
{
Cue = new Cue(1),
Setters =
{
new Setter(OpacityProperty, 0.0),
new Setter(ScaleTransform.ScaleXProperty, 1.5)
}
}
}
};
await animation.RunAsync(myButton);
}
Cancellable Animations
private CancellationTokenSource? _animationCts;
public async Task StartAnimation()
{
_animationCts?.Cancel();
_animationCts = new CancellationTokenSource();
try
{
await animation.RunAsync(control, _animationCts.Token);
}
catch (OperationCanceledException)
{
// Animation was cancelled
}
}
public void StopAnimation()
{
_animationCts?.Cancel();
}
Use await animation.RunAsync() for one-time animations that need to complete. For continuous animations, set IterationCount to IterationCount.Infinite and use Apply() instead.
Controlling Animation Lifecycle
var subscription = animation.Apply(
control,
clock: null,
match: Observable.Return(true),
onComplete: () => Console.WriteLine("Animation completed")
);
// Later: stop the animation
subscription.Dispose();
Page Transitions
Page transitions animate content changes in navigation scenarios.
CrossFade Transition
<TransitioningContentControl>
<TransitioningContentControl.PageTransition>
<CrossFade Duration="0:0:0.3"/>
</TransitioningContentControl.PageTransition>
</TransitioningContentControl>
PageSlide Transition
<TransitioningContentControl>
<TransitioningContentControl.PageTransition>
<PageSlide Duration="0:0:0.5" Orientation="Horizontal"/>
</TransitioningContentControl.PageTransition>
</TransitioningContentControl>
Composite Page Transition
<TransitioningContentControl>
<TransitioningContentControl.PageTransition>
<CompositePageTransition>
<CrossFade Duration="0:0:0.2"/>
<PageSlide Duration="0:0:0.3" Orientation="Horizontal"/>
</CompositePageTransition>
</TransitioningContentControl.PageTransition>
</TransitioningContentControl>
Animation Best Practices
Animate GPU-accelerated properties for best performance:
Opacity
TranslateTransform (X, Y)
RotateTransform (Angle)
ScaleTransform (ScaleX, ScaleY)
These properties can be animated without triggering layout or re-rendering of parent controls.
Avoid animating these properties as they can cause performance issues:
Width / Height - Triggers layout on every frame
Margin - Affects parent layout
FontSize - Requires text re-rendering
Instead, use ScaleTransform to achieve similar visual effects.
Transition Lifecycle
From ~/workspace/source/src/Avalonia.Base/Animation/Animatable.cs, transitions are automatically:
- Enabled when a control is added to the visual tree
- Disabled when a control is removed from the visual tree
This prevents unnecessary animations on controls that aren’t visible.
Memory Management
// Store animation subscriptions and dispose when done
private readonly CompositeDisposable _animationDisposables = new();
public void StartAnimations()
{
_animationDisposables.Add(animation1.Apply(control1, ...));
_animationDisposables.Add(animation2.Apply(control2, ...));
}
protected override void OnDetachedFromVisualTree()
{
_animationDisposables.Dispose();
base.OnDetachedFromVisualTree();
}
Advanced Animation Techniques
Custom Animators
Create custom animators for types not supported out of the box:
using Avalonia.Animation.Animators;
public class CustomTypeAnimator : Animator<CustomType>
{
public override CustomType Interpolate(double progress, CustomType oldValue, CustomType newValue)
{
// Implement custom interpolation logic
return new CustomType(
oldValue.Value + (newValue.Value - oldValue.Value) * progress
);
}
}
FillMode Behavior
Control how animated values are applied before and after the animation:
None - Property reverts to base value before and after
Forward - Final animated value persists after animation
Backward - Initial animated value applies during delay
Both - Both Forward and Backward behaviors
<Animation Duration="0:0:2" FillMode="Both" Delay="0:0:1">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="0"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="1"/>
</KeyFrame>
</Animation>
PlaybackDirection Options
<!-- Normal: 0% → 100% -->
<Animation PlaybackDirection="Normal" IterationCount="3"/>
<!-- Reverse: 100% → 0% -->
<Animation PlaybackDirection="Reverse" IterationCount="3"/>
<!-- Alternate: 0% → 100% → 0% → 100% -->
<Animation PlaybackDirection="Alternate" IterationCount="4"/>
<!-- AlternateReverse: 100% → 0% → 100% → 0% -->
<Animation PlaybackDirection="AlternateReverse" IterationCount="4"/>
See Also