| name | advanced-motion-easing |
| description | Production-grade motion engineering from 20 repos. Spring physics, custom easing curves, GPU compositing rules, scroll-driven animations, micro-interactions, Lottie integration, reduced-motion, and stagger choreography. Sources: framer/motion, pmndrs/react-spring, popmotion, animejs, GSAP, auto-animate, @formkit/drag-and-drop, lottie-web, use-gesture, motion-canvas, react-transition-group, swiper, embla-carousel, scrollreveal, lax.js, use-spring, react-use-gesture, theatre.js, rive-app/rive-wasm, css-spring-easing. |
/advanced-motion-easing
When to Use
- Implementing spring-based UI transitions (not CSS ease-in-out)
- Building drag-and-drop with inertia
- Scroll-driven animations that feel native
- Choreographing staggered list entrances
- Integrating Lottie / Rive animations with code control
Do NOT use for
- Static pages with no interaction
- Transitions < 150ms (imperceptible — skip animation entirely)
Spring Config Reference (framer/motion + react-spring)
const springs = {
snappy: { stiffness: 400, damping: 30, mass: 1 },
default: { stiffness: 260, damping: 25, mass: 1 },
gentle: { stiffness: 120, damping: 20, mass: 1 },
wobbly: { stiffness: 180, damping: 12, mass: 1 },
slow: { stiffness: 60, damping: 15, mass: 1 },
}
<motion.div animate={{ y: 0 }} transition={springs.snappy} />
const { y } = useSpring({ y: 0, config: springs.gentle })
Custom Easing Curves (css-spring-easing + GSAP)
import { createEasing } from 'css-spring-easing'
const [easing, duration] = createEasing({ stiffness: 260, damping: 25 })
element.animate({ transform: ['translateY(20px)', 'translateY(0)'] }, {
duration,
easing,
fill: 'both',
})
GPU Compositing Rules (framer/motion + GSAP guidelines)
✅ Animate: transform (translate, scale, rotate), opacity
✅ Use will-change: transform sparingly — only during animation
✅ Use translateZ(0) or translate3d to force GPU layer
❌ Never animate: width, height, top, left, margin, padding
→ triggers layout reflow on every frame
❌ Never animate: background-color, box-shadow
→ triggers paint; use opacity + pseudo-element trick instead
// Correct shadow animation
.card::after {
opacity: 0;
box-shadow: 0 8px 24px rgba(0,0,0,.2);
transition: opacity 200ms;
}
.card:hover::after { opacity: 1; }
Scroll-Driven Animations (CSS native)
@keyframes reveal {
from { opacity: 0; translate: 0 40px; }
to { opacity: 1; translate: 0 0; }
}
.card {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% entry 40%;
}
#progress {
position: fixed;
transform-origin: left;
animation: grow linear;
animation-timeline: scroll(root block);
}
@keyframes grow { to { transform: scaleX(1); } }
Stagger Choreography (anime.js)
import anime from 'animejs'
anime({
targets: '.list-item',
opacity: [0, 1],
translateY: [16, 0],
easing: 'spring(1, 80, 10, 0)',
delay: anime.stagger(50, { start: 0 }),
duration: 350,
})
Lottie Integration (lottie-web)
import lottie from 'lottie-web'
const anim = lottie.loadAnimation({
container: document.getElementById('lottie'),
renderer: 'svg',
loop: false,
autoplay: false,
path: '/animations/success.json',
})
anim.addEventListener('DOMLoaded', () => {
anim.goToAndStop(0, true)
})
function playOnHover() { anim.play() }
function reset() { anim.goToAndStop(0, true) }
Reduced-Motion (mandatory — framer/motion hook)
import { useReducedMotion } from 'framer-motion'
function AnimatedCard() {
const reduceMotion = useReducedMotion()
return (
<motion.div
initial={{ opacity: reduceMotion ? 1 : 0, y: reduceMotion ? 0 : 20 }}
animate={{ opacity: 1, y: 0 }}
transition={reduceMotion ? { duration: 0 } : springs.default}
/>
)
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
Anti-Fake-Pass Checklist
❌ Animating width/height/top/left (layout thrash)
❌ Stagger total > 800ms (items beyond 8 at 50ms stagger)
❌ will-change: transform left on permanently (memory leak)
❌ No prefers-reduced-motion guard
❌ Lottie using canvas renderer for < 60fps SVG-quality use case
❌ CSS transition on box-shadow (use opacity + ::after trick)
❌ Spring damping < 10 (infinite oscillation)