From maui-skills
Animates .NET MAUI views using fade, scale, rotation, translation, easing functions, and chaining. Addresses common pitfalls like uncancelled animations, accessibility with reduced motion, and performance tips.
npx claudepluginhub davidortinau/maui-skills --plugin maui-skillsThis skill uses the workspace's default tool permissions.
Running multiple animations on the same property causes visual glitches.
Creates WPF animations using Storyboard, Timeline, and EasingFunction patterns for UI transitions, state change visualizations, and interactive feedback effects.
Writes Swift animation code using SwiftUI, UIKit, and Core Animation for iOS apps. Covers iOS 18-26 APIs like KeyframeAnimator, PhaseAnimator, matchedGeometryEffect for transitions, gestures, and design implementations.
Creates, reviews, and debugs UI animations including springs, gestures, drag interactions, clip-path reveals, easing, timing, CSS transitions, keyframes, and Framer Motion. Use for motion design, smooth implementations, or reviews.
Share bugs, ideas, or general feedback.
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);
}
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") 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);
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
rate: 16 in Commit is one frame at 60fps. Complex callbacks cause jank.WidthRequest, HeightRequest) — use TranslationX/Y and Scale instead.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));
| 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 |