mcpField
M
maui-animations

maui-animations

github.com/davidortinauDesign & UI17661 installsUpdated 16w ago
Repository

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

  1. 01Prevent and cancel overlapping animations
  2. 02Configure custom animation repetition
  3. 03Respect reduced-motion settings
  4. 04Guidance on parallel animation and performance
  5. 05Choose easing for interface effects

Use cases

  1. 01Fix visual glitches from rapidly triggered animations
  2. 02Support users who prefer reduced motion
  3. 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-animations

After 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: 16 in Commit is one frame at 60fps. Complex callbacks cause jank.
  • Avoid animating layout-triggering properties (WidthRequest, HeightRequest) — use TranslationX/Y and Scale instead.
  • Use Task.WhenAll for parallel animations — sequential await chains 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

GoalEasingWhy
Button feedbackCubicOutQuick deceleration feels responsive
Page transitionsCubicInOutSmooth start and end
Bouncing elementsBounceOutPlayful, attention-grabbing
Spring effectsSpringOutNatural, elastic feel
Loading indicatorsLinearConstant speed for continuous motion
Entry/exitSinIn / SinOutSubtle, non-distracting

Comments

Markdown supported