Motion.Avalonia lets stable Avalonia controls move between unrelated live layouts. You author ordinary panel trees with
MotionSlot placeholders, then choose a motion model that fits the input:
StateMotiontransitions between complete named states;SequenceMotionsamples sparse keyframes from normalized progress;TimelineMotionsamples sparse keyframes at absolute times and provides playback.
The framework performs real Measure and Arrange work. Text reflows at intermediate widths, bindings remain live,
and resizing immediately changes the endpoints. It does not render controls to bitmaps or use scale transforms to
imitate layout.
- Stable target control identity across every layout
- Live, responsive
DataTemplatelayout sources - Real intermediate measure and arrange
- Smooth interruption and retargeting for named states
- Seekable progress and absolute-time keyframes
- Sparse per-property tracks
- Layout and styled properties on one interpolation pipeline
- Directed state-transition timing overrides
- Strongly typed custom interpolators
- Reuse of Avalonia's built-in
Animator<T>implementations - Nested target styling through Avalonia selector syntax
- Relative Z-order evaluation
- Animation-priority bindings that restore underlying values when motion ends
- No bitmap snapshots or transform-based pseudo-layout
Install the package:
dotnet add package Motion.AvaloniaDefine a StateMotion and keep the real controls as direct children:
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:motion="using:Motion.Avalonia">
<motion:MotionPanel>
<motion:MotionPanel.Motion>
<motion:StateMotion
x:Name="EditorMotion"
State="Quick"
Duration="0:0:0.35"
Easing="CubicEaseInOut">
<motion:MotionState Key="Quick">
<DataTemplate>
<Grid>
<motion:MotionSlot
x:Name="Editor"
MaxWidth="600"
HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</motion:MotionState>
<motion:MotionState Key="Advanced">
<DataTemplate>
<DockPanel HorizontalSpacing="24">
<motion:MotionSlot
x:Name="Editor"
MaxWidth="420"
DockPanel.Dock="Left"/>
<motion:MotionSlot x:Name="Preview"/>
</DockPanel>
</DataTemplate>
</motion:MotionState>
</motion:StateMotion>
</motion:MotionPanel.Motion>
<Border x:Name="Editor"/>
<Border x:Name="Preview"/>
</motion:MotionPanel>
</UserControl>Change EditorMotion.State to "Advanced" to begin the transition.
Three rules connect templates to real controls:
- Every target is a stable direct child of
MotionPanel. - Every target has a unique, non-empty
x:Name. - A
MotionSlotuses the samex:Nameas the target it represents.
Continue with Getting Started for a complete example.
Use StateMotion when the application selects complete layouts such as compact/expanded, quick/advanced, or
editing/review. Missing slots are absent from that state.
Use SequenceMotion when another component owns normalized progress:
<motion:SequenceMotion Progress="{Binding #ProgressSlider.Value}">
<motion:SequenceMotionKeyFrame Cue="0%">
<DataTemplate>
<motion:MotionSlot x:Name="Card" HorizontalAlignment="Left"/>
</DataTemplate>
</motion:SequenceMotionKeyFrame>
<motion:SequenceMotionKeyFrame Cue="100%" Easing="CubicEaseOut">
<DataTemplate>
<motion:MotionSlot x:Name="Card" HorizontalAlignment="Right"/>
</DataTemplate>
</motion:SequenceMotionKeyFrame>
</motion:SequenceMotion>Keyframes are sparse: an omitted property receives no key at that Cue.
Use TimelineMotion for absolute KeyTime values. Bind or assign Time to scrub, or call
TimelineMotion.Player.Play() for frame-aligned playback. Loop controls wrapping and Duration is derived from the
latest keyframe.
MotionPanel targets + live layout sources
│
▼
Motion.Compile(context)
│
▼
Long-lived per-property tracks
│
▼
Easing → MotionInterpolator → Animation-priority binding
│
▼
Stable containers and real controls
Compiling a motion discovers its layout and property channels once. Progress or time changes update the existing tracks;
they do not rebuild the target controls. MotionPanel remains responsible for measuring live layout sources and
supplying their current bounds to layout tracks.
| Chapter | Description |
|---|---|
| Getting Started | Build a complete two-mode editor transition |
| Core Concepts | Learn targets, layout sources, channels, and presence |
| States and Slots | Author responsive complete-state layouts |
| Motions and Keyframes | Choose State, Sequence, or Timeline motion |
| Target Properties | Use shortcuts, setters, bindings, and target styles |
| Interpolators | Customize timing and value-space interpolation |
| Recipes | Apply the primitives to common UI patterns |
| Architecture | Understand Apply, tracks, layout frames, and lifetime |
| Limitations and Troubleshooting | Diagnose scope and configuration errors |
| API Reference | Look up public types and properties |
Motion.Avalonia currently targets stable direct children of one MotionPanel. Cross-window, cross-page, and
cross-ancestor shared-element transitions are outside the current implementation. Direct Avalonia properties cannot be
animated. The API is an early preview and may change as physics, interaction, and higher-level animation systems are
added.
Motion.Avalonia— reusable motion, layout, player, and interpolation primitivesMotion.Avalonia.Demo— shared cross-platform showcase UIMotion.Avalonia.Demo.Desktop— Windows, macOS, and Linux hostMotion.Avalonia.Demo.Android— Android hostMotion.Avalonia.Demo.iOS— iPhone and iPad hostMotion.Avalonia.Demo.Browser— WebAssembly hostMotion.Avalonia.Tests— behavior and lifecycle tests
Run the desktop showcase:
dotnet run --project src/Motion.Avalonia.Demo.DesktopIssues, design discussions, examples, and pull requests are welcome. Describe the user-facing motion problem as well as the proposed implementation.
Motion.Avalonia is distributed under the MIT License.