About maui-animations
A skill for .NET MAUI developers covering animation behavior, accessibility, performance, and easing choices. It focuses on practical pitfalls such as cancellation, repeat callbacks, and matching animation names when aborting.
Key features
- 01Prevent and cancel overlapping animations
- 02Configure custom animation repetition
- 03Respect reduced-motion settings
- 04Guidance on parallel animation and performance
- 05Choose easing for interface effects
Use cases
- 01Fix visual glitches from rapidly triggered animations
- 02Support users who prefer reduced motion
- 03Plan parallel interface animations
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ davidortinau/ maui-skills --skill maui-animationsAfter installing, type/maui-animationsin Claude Code to confirm it loaded.
Bundled filesreferences/animations-api.md
Details
From the project README.NET MAUI Animations
Common Mistakes
❌ Forgetting to cancel before starting new animations
Running multiple animations on the same property causes visual glitches.
// ❌ If called rapidly, animations queue and overlap
async void OnButtonClicked()
{
await view.FadeTo(0, 500);
await view.FadeTo(1, 500);
}
// ✅ Cancel first, then animate
async void OnButtonClicked()
{
view.CancelAnimations();
await view.FadeTo(0, 500);
await view.FadeTo(1, 500);
}
❌ Custom Animation repeat callback misconception
Returning true from a child animation's repeat callback does NOT repeat the parent. Only the repeat callback passed to Commit on the parent controls parent repetition.
// ❌ This does NOT loop the parent animation
parent.Add(0.0, 1.0, new Animation(v => view.Scale = v, 1, 2));
parent.Commit(view, "MyAnim", length: 1000,
repeat: () => false); // Parent won't loop
// ✅ Loop the parent by passing repeat to Commit
parent.Commit(view, "MyAnim", length: 1000,
repeat: () => true); // This loops the entire animation
❌ AbortAnimation name mismatch
AbortAnimation("name") must match the exact string passed to Commit. A mismatch silently does nothing.
// ❌ Name mismatch — animation keeps running
parent.Commit(view, name: "MyAnimation", length: 1000);
view.AbortAnimation("myAnimation"); // Wrong case!
// ✅ Use a constant for the name
const string AnimName = "MyAnimation";
parent.Commit(view, name: AnimName, length: 1000);
view.AbortAnimation(AnimName);
Accessibility: Respect Reduced Motion
Always check IsAnimationEnabled before running animations. It is false when the OS power-save / reduced-motion mode is active.
// ❌ Ignores user's accessibility preference
await view.FadeTo(1, 500);
// ✅ Respects reduced-motion settings
if (view.IsAnimationEnabled)
await view.FadeTo(1, 500);
else
view.Opacity = 1; // Jump to final state instantly
Performance Tips
- Keep animation callbacks under 16ms — the default
rate: 16inCommitis one frame at 60fps. Complex callbacks cause jank. - Avoid animating layout-triggering properties (
WidthRequest,HeightRequest) — useTranslationX/YandScaleinstead. - Use
Task.WhenAllfor parallel animations — sequentialawaitchains are slower.
// ❌ Sequential — takes 1500ms total
await view.FadeTo(1, 500);
await view.ScaleTo(1.5, 500);
await view.RotateTo(360, 500);
// ✅ Parallel — takes 500ms total
await Task.WhenAll(
view.FadeTo(1, 500),
view.ScaleTo(1.5, 500),
view.RotateTo(360, 500));
Easing Selection Guide
| Goal | Easing | Why |
|---|---|---|
| Button feedback | CubicOut | Quick deceleration feels responsive |
| Page transitions | CubicInOut | Smooth start and end |
| Bouncing elements | BounceOut | Playful, attention-grabbing |
| Spring effects | SpringOut | Natural, elastic feel |
| Loading indicators | Linear | Constant speed for continuous motion |
| Entry/exit | SinIn / SinOut | Subtle, non-distracting |