Moro
Gallery
IFoundations
01Why motion exists in UI
02Timing & duration
03Easing
04Springs vs tweens
IIExpression
05Anticipation
06Follow-through & overlapping action
07Squash, stretch & impact
08Arcs
IIIEngineering
09Performance
10Layout animations & FLIP
11Interruptibility & interaction
12Accessibility & restraint
Tier IIIEngineering11

Interruptibility & interaction

The best animations let users change their mind mid-motion

Tap a button. Before the animation finishes, tap it again. What happens? In a well-built interface, the second tap is respected immediately. In a naively-built one, the first animation completes first: an unresponsive moment, often described as "feeling laggy" even when every individual animation is fast. Interruptibility is the gap between motion that plays and motion that responds.

#The idea

Animations take time. During that time, users might want to do something else: tap again, drag, dismiss, change direction. A good motion system treats animation as a proposal that can be overridden, not a commitment that must complete.

Two properties matter:

  • Input latency: when a user interacts during an animation, how long before the UI acknowledges the new input? Ideally zero. The old animation should cancel (or redirect) and the new one should start immediately.
  • Velocity preservation: if the user's input has momentum (a drag, a flick), the animation that follows should continue with that velocity rather than starting from zero. This is why springs (lesson 4) are so much better than tweens for gestures: springs take velocity as an input.

Most CSS animations are not interruptible. A @keyframes animation played on an element will typically run to completion, and re-triggering it restarts from frame zero. That's fine for decorative motion, fatal for interactive motion. This is one of the main reasons production interaction UIs use JavaScript (Motion, spring libraries): JS animations can be canceled, redirected, or handed to the next gesture mid-flight.

Canonical example

Drag an iOS notification down, but release mid-drag. The notification springs back to its starting position. Now drag it down again before it fully settles: notice how the second drag picks up seamlessly, with the element continuing from its current position and velocity. There's no jump, no restart. Every major iOS gesture has this property, and it's the single biggest reason iOS feels so alive compared to most web apps.

Drag and release. The spring physics carry momentum. Interrupt mid-settle and the new drag picks up from wherever you grabbed.

Controls

0.10
300.00
20.00

Hover effects should feel instant, and they do, because Motion cancels in-flight animations on state change.

Controls

1.15
5.00°
400.00
25.00

Tap the button repeatedly. Each tap registers immediately, even while the previous rotation is still settling.

Controls

0.90
1.05
300.00
25.00

#When it applies

  • All gesture-driven motion: drag, flick, swipe, pinch. Users expect their input to be respected instantly.
  • Rapid-toggle interactions: hover on/off, tap repeatedly, back-and-forth navigation.
  • Anywhere a user might cancel their action: drag-to-dismiss that might be released halfway.

#When it doesn't

  • System-initiated motion that shouldn't be user-interruptible: a confirmation toast shouldn't be draggable; a loading indicator shouldn't stop on tap.
  • Critical sequences: error shakes, validation animations. These should play to completion so users don't miss them.
  • Onboarding flows: guided moments where you want attention on a specific animation; let it play.
Common mistake

Using CSS @keyframes for interactive animations. Any animation that responds to user input should be JavaScript-driven (Motion, React Spring, your own state-driven transform). CSS keyframes are great for decoration; they are not great for interaction.

#Implementation

Motion's state changes cancel in-flight animations and start new ones from the current value:

InteractiveButton.tsx
// This just works:
// If the user hovers, unhovers, and hovers again quickly,
// each state change cancels the previous animation and starts fresh.
// Velocity is preserved through the motion value.
 
<motion.div
  whileHover={{ scale: 1.1 }}
  whileTap={{ scale: 0.95 }}
  transition={{ type: "spring", stiffness: 400, damping: 25 }}
/>

Using motion values for fully interruptible, imperative control:

DraggableBox.tsx
const x = useMotionValue(0)
const springX = useSpring(x, { stiffness: 300, damping: 30 })
 
// Any update to x is smoothly tracked by springX,
// including mid-animation changes.
<motion.div style={{ x: springX }} drag="x" />

useSpring wraps a motion value and applies physics. The output tracks the input smoothly, which is the basis of every velocity-preserving gesture.

#Craft notes

  • Gestures should always use springs. Tweens lose velocity context on interruption; springs preserve it.
  • Give users a visible cancel. Drag-to-dismiss should spring back clearly if released mid-drag. Invisible cancel feels like the system didn't register the release.
  • Animate on requestAnimationFrame, not React state, where possible. Motion values bypass React renders, which means zero dropped frames even at high input rates.
  • Keep input-driven state outside component state. useMotionValue + useTransform gives you reactive animation values without triggering re-renders. This is Motion's main performance win over naive approaches.

#Exercises

  1. Break an animation. Find an animation in your project that doesn't interrupt well. Re-tap the trigger rapidly. Does it feel laggy? Convert it to a spring with velocity preservation and retry.
  2. Compare velocity handling. Build two drag demos: one that releases to a tween, one to a spring. Flick both and release. The tween ignores the flick velocity; the spring honors it.
  3. Study a gesture. Open iOS Control Center, drag it halfway, release, immediately grab it again. Describe what the system does and articulate why it feels coherent.

#Study this in the wild

  • iOS Dynamic Island. Every interaction is interruptible. The entire thing is spring-based.
  • Arc browser tab drag. Drag a tab and change direction mid-drag. Notice velocity preservation in real time.
  • macOS dock magnification. Sweep the cursor back and forth across the dock rapidly. Each icon responds to current position, not the previous frame.

#Further reading

  • Motion: Motion values.
  • Matt Perry: How Framer Motion handles gestures.
  • Rauno Freiberg: The feel of interaction.
Lesson 10
Layout animations & FLIP
Lesson 12
Accessibility & restraint

On this page

The ideaWhen it appliesWhen it doesn'tImplementationCraft notesExercisesStudy this in the wildFurther reading

Built by David Umoru