Framer Motion
React animation library: variants, gestures, layout animations, scroll, exit.
framer-motionanimationreactfrontend
# Framer Motion
## Install
```bash
npm install framer-motion
```
## Basic motion
```tsx
import { motion } from 'framer-motion'
// Animate on mount
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, ease: 'easeOut' }}
/>
// Hover + tap
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
/>
```
## Variants (stagger children)
```tsx
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: { staggerChildren: 0.1 },
},
}
const item = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0 },
}
<motion.ul variants={container} initial="hidden" animate="show">
{items.map(i => (
<motion.li key={i} variants={item}>{i}</motion.li>
))}
</motion.ul>
```
## AnimatePresence (exit animations)
```tsx
import { AnimatePresence, motion } from 'framer-motion'
<AnimatePresence>
{isVisible && (
<motion.div
key="modal"
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
/>
)}
</AnimatePresence>
```
## Layout animations
```tsx
// Automatically animate layout changes
<motion.div layout />
// Shared layout transition between routes
<motion.div layoutId="hero-image" />
// On next page, same layoutId -> morphs between positions
```
## Scroll animations
```tsx
import { useScroll, useTransform, motion } from 'framer-motion'
function ParallaxHero() {
const { scrollY } = useScroll()
const y = useTransform(scrollY, [0, 500], [0, -150])
return <motion.img style={{ y }} src="/hero.jpg" />
}
// Viewport entrance
<motion.section
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true, margin: '-100px' }}
/>
```
## useAnimate (imperative)
```tsx
import { useAnimate } from 'framer-motion'
const [scope, animate] = useAnimate()
await animate(scope.current, { x: 100 }, { duration: 0.5 })
await animate('.child', { opacity: 0 }, { delay: stagger(0.1) })
```
## Gestures
```tsx
<motion.div
drag
dragConstraints={{ left: -100, right: 100, top: -50, bottom: 50 }}
dragElastic={0.1}
onDragEnd={(e, info) => console.log(info.offset)}
/>
```API: /api/skills/framer-motion