Motion pentru React
motion.div, initial/animate/exit, AnimatePresence, variants și stagger, layout, scroll, spring.
Actualizat
Ce este Motion
Motion (fostul Framer Motion) e librăria de animații cea mai folosită în React. Ideea: înlocuiești un tag HTML cu varianta lui motion. și descrii stările, nu pașii — Motion calculează animația dintre ele.
npm i motion'use client'
import { motion } from 'motion/react'
export function Card() {
return <motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.3 }} />
}Componentele motion.* sunt Client Components — fișierul are 'use client'. Poți păstra pagina ca Server Component și să animezi doar bucăți mici.
Proprietățile de bază
| Prop | Ce face |
|---|---|
initial |
starea de pornire (la montare) |
animate |
starea țintă — când se schimbă, Motion animă spre ea |
exit |
starea la demontare (necesită AnimatePresence) |
transition |
duration, ease, delay, sau type: 'spring' cu stiffness / damping / bounce |
whileHover, whileTap, whileFocus |
stări temporare la interacțiune |
whileInView |
stare când elementul intră în ecran (+ viewport={{ once: true }}) |
layout |
animă automat schimbările de mărime / poziție din layout |
layoutId |
două elemente diferite cu același id se „transformă” unul în altul |
Ieșiri: AnimatePresence
React scoate imediat un element din DOM când nu mai e randat — nu e timp de animație. AnimatePresence îl ține până se termină exit:
<AnimatePresence>
{open && (
<motion.div key="panel" initial={{ opacity: 0, y: -8 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -8 }} />
)}
</AnimatePresence>Variants și stagger
Variants = stări cu nume, definite o dată și propagate copiilor. Ideal pentru liste:
import { motion, stagger } from 'motion/react'
const list = { hidden: {}, show: { transition: { delayChildren: stagger(0.06) } } }
const item = { hidden: { opacity: 0, y: 16 }, show: { opacity: 1, y: 0 } }
<motion.ul variants={list} initial="hidden" whileInView="show" viewport={{ once: true }}>
{products.map(p => <motion.li key={p.id} variants={item}>{p.name}</motion.li>)}
</motion.ul>Copiii nu au nevoie de initial / animate — moștenesc numele stărilor de la părinte.
Layout animations — efectul „wow” cu o linie
<motion.div layout className={expanded ? 'w-full' : 'w-40'} /> // mărimea se animă automat{tabs.map(tab => (
<button key={tab} onClick={() => setActive(tab)} className="relative px-3 py-1">
{active === tab && <motion.span layoutId="tab-indicator" className="absolute inset-0 rounded-md bg-hover" />}
<span className="relative">{tab}</span>
</button>
))}Indicatorul „alunecă” de la un tab la altul — fără niciun calcul de poziție.
Scroll
const { scrollYProgress } = useScroll() // 0 → 1 pe toată pagina
const scaleX = useSpring(scrollYProgress) // netezit cu spring
<motion.div style={{ scaleX }} className="fixed inset-x-0 top-0 h-1 origin-left bg-accent" /> // bară de progres
const y = useTransform(scrollYProgress, [0, 1], [0, -200]) // parallaxuseTransform = interpolare: mapează un interval de intrare pe unul de ieșire.
Spring vs tween
Tween (duration + ease) |
Spring | |
|---|---|---|
| Durată | fixă | rezultă din fizică |
| Senzație | controlată, precisă | naturală, „vie” |
| Întrerupere la mijloc | poate sări | continuă lin, păstrează viteza |
| Când | opacitate, culoare, fade-uri | mișcare, mărime, drag, gesturi |
Pe scurt
motion.div+initial/animate/exit;AnimatePresencepentru ieșiri.- Variants +
stagger()pentru liste;whileInViewpentru apariții la scroll. layout/layoutIdpentru tranziții de layout;useScroll+useTransformpentru scroll; spring pentru mișcare.