Skip to main content
Version: 5.3.2

withDecay

The withDecay modifier simulates natural momentum with friction. It's perfect for scenarios where you want values to gradually slow down, like a thrown ball losing speed or a scroll that decelerates naturally.

Why Use withDecay?

Decay animations feel natural because they:

  • Simulate physics - Realistic momentum and friction
  • Feel responsive - Natural slowdown after gestures
  • Work great with gestures - Perfect for drag, scroll, and wheel interactions
  • No fixed destination - Values slow down naturally rather than reaching a target

Basic Syntax

withDecay(velocity, options?)

Parameters

  • velocity (required): The initial velocity. Higher values = faster initial movement.
  • options (optional): Configuration object for decay behavior.

Simple Example

The simplest decay animation:

import { useValue, withDecay, animate } from 'react-ui-animate';

function MyComponent() {
const [x, setX] = useValue(0);

return (
<>
<button onClick={() => setX(withDecay(0.5))}>
Start Decay
</button>
<animate.div
style={{
translateX: x,
width: 100,
height: 100,
background: '#60a5fa',
}}
/>
</>
);
}

Configuration Options

OptionTypeDefaultDescription
onStart() => void-Called when animation starts
onChange(value) => void-Called on every frame with current value
onComplete() => void-Called when animation finishes

Real-World Examples

Example 1: Drag with Momentum

Perfect for draggable elements that continue moving after release:

import { useValue, useDrag, withDecay, animate } from 'react-ui-animate';

function DraggableWithMomentum() {
const ref = useRef(null);
const [x, setX] = useValue(0);
const [velocity, setVelocity] = useValue(0);

useDrag(ref, ({ down, movement, velocity: dragVelocity }) => {
if (down) {
setX(movement.x);
setVelocity(dragVelocity.x);
} else {
// Apply decay with the drag velocity
setX(withDecay(velocity * 0.1));
}
});

return (
<animate.div
ref={ref}
style={{
translateX: x,
width: 100,
height: 100,
background: '#60a5fa',
cursor: 'grab',
}}
/>
);
}

Example 2: Scroll Momentum

Simulate natural scroll deceleration:

function ScrollableContent() {
const [scrollY, setScrollY] = useValue(0);
const [velocity, setVelocity] = useValue(0);

useScroll(window, ({ scroll, velocity: scrollVelocity }) => {
setScrollY(scroll.y);
setVelocity(scrollVelocity.y);
});

const applyMomentum = () => {
// Continue scrolling with decay
setScrollY(withDecay(velocity * 0.05));
};

return (
<div>
<button onClick={applyMomentum}>Apply Momentum</button>
<animate.div
style={{
translateY: -scrollY,
}}
>
{/* Content */}
</animate.div>
</div>
);
}

Example 3: Flick Gesture

Create a flickable card:

function FlickableCard() {
const ref = useRef(null);
const [x, setX] = useValue(0);
const [velocity, setVelocity] = useValue(0);

useDrag(ref, ({ down, movement, velocity: dragVelocity }) => {
if (down) {
setX(movement.x);
setVelocity(dragVelocity.x);
} else {
// Flick with decay based on drag velocity
if (Math.abs(velocity) > 0.5) {
setX(withDecay(velocity * 0.15));
} else {
// Snap back if velocity is too low
setX(withSpring(0));
}
}
});

return (
<animate.div
ref={ref}
style={{
translateX: x,
width: 200,
height: 200,
background: '#60a5fa',
borderRadius: 8,
cursor: 'grab',
}}
>
Flick me
</animate.div>
);
}
Preview

Understanding Velocity

Velocity determines how fast the animation starts:

  • Low velocity (0.1-0.3) - Slow, gentle deceleration
  • Medium velocity (0.5-1.0) - Moderate speed, natural feel
  • High velocity (1.5+) - Fast start, longer deceleration

Using with Gestures

withDecay works perfectly with gesture hooks:

useDrag(ref, ({ down, movement, velocity }) => {
if (down) {
setX(movement.x);
} else {
// Use drag velocity for natural momentum
setX(withDecay(velocity.x * 0.1));
}
});

Using Callbacks

Track decay progress:

function DecayAnimation() {
const [x, setX] = useValue(0);

const startDecay = () => {
setX(withDecay(0.5, {
onStart: () => {
console.log('Decay started');
},
onChange: (value) => {
console.log('Current position:', value);
},
onComplete: () => {
console.log('Decay complete - stopped');
},
}));
};

return (
<>
<button onClick={startDecay}>Start Decay</button>
<animate.div
style={{
translateX: x,
width: 100,
height: 100,
background: '#60a5fa',
}}
/>
</>
);
}

Common Patterns

Pattern 1: Drag and Release

useDrag(ref, ({ down, movement, velocity }) => {
if (down) {
setX(movement.x);
} else {
// Continue with momentum
setX(withDecay(velocity.x * 0.1));
}
});

Pattern 2: Scroll Momentum

useScroll(container, ({ velocity }) => {
// Apply decay when scroll stops
setScrollPosition(withDecay(velocity.y * 0.05));
});

Pattern 3: Flick to Dismiss

useDrag(ref, ({ down, movement, velocity }) => {
if (down) {
setX(movement.x);
} else {
// Flick with high velocity = dismiss
if (Math.abs(velocity.x) > 1.0) {
setX(withDecay(velocity.x * 0.2));
// Dismiss when far enough
if (Math.abs(x) > 200) {
onDismiss();
}
} else {
// Snap back
setX(withSpring(0));
}
}
});

Best Practices

✅ Do

  • Use withDecay with gesture hooks for natural momentum
  • Adjust velocity multiplier based on your needs (0.05-0.2 is common)
  • Combine with withSpring for snap-back behavior
  • Use for scroll, drag, and wheel interactions

❌ Don't

  • Don't use withDecay for fixed destination animations (use withSpring or withTiming)
  • Don't set velocity too high - can cause janky animations
  • Don't forget to handle edge cases (snap back, boundaries)
  • Don't use for simple show/hide animations

Performance Tips

  1. Decay is optimized - Very performant for gesture-based animations
  2. Use transforms - translateX, translateY are GPU-accelerated
  3. Combine with gestures - Works best with useDrag, useScroll, useWheel
  4. Set boundaries - Prevent values from going too far

Troubleshooting

Animation doesn't slow down:

  • Check that velocity is being set correctly
  • Verify you're using withDecay correctly

Animation stops too quickly:

  • Increase velocity multiplier
  • Check if boundaries are cutting off the animation

Animation feels janky:

  • Make sure you're using transforms, not layout properties
  • Reduce velocity if too high

Next Steps

  • Learn about useDrag for drag interactions
  • Explore useScroll for scroll-based animations
  • Check out withSequence to combine decay with other animations