动画与 Framer Motion
过渡与手势基础。
25 分钟
动画与 Framer Motion
React 更新 DOM 很快,但 进出场、布局变化、手势 用 CSS alone 难协调。Framer Motion(现 Motion for React)提供声明式 motion 组件 与 layout 动画,是 React 生态最常用的动画方案之一。
学习目标
- 掌握
motion、AnimatePresence基本用法 - 理解何时用 CSS transition、何时用 Motion
- 避免动画损害性能与无障碍
CSS vs Framer Motion
| 场景 | CSS transition | Framer Motion |
|---|---|---|
| hover 颜色 | ✅ 足够 | 过重 |
| 列表增删 reorder | 难 | layout + AnimatePresence |
| 路由页切换 | 需手写 key | 与 React 生命周期对齐 |
| 拖拽 | 需自己算 | drag prop |
基础过渡
import { motion } from "framer-motion";
export function FadeIn({ children }: { children: React.ReactNode }) {
return (
<motion.div
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.25 }}
>
{children}
</motion.div>
);
}
initial / animate / exit 三元组是核心;退出动画需外层 AnimatePresence。
列表与 AnimatePresence
import { AnimatePresence, motion } from "framer-motion";
<AnimatePresence mode="popLayout">
{items.map((item) => (
<motion.li
key={item.id}
layout
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
{item.title}
</motion.li>
))}
</AnimatePresence>
layout 让 sibling 尺寸变化时 平滑重排,适合侧栏折叠、卡片网格。
手势基础
<motion.div drag="x" dragConstraints={{ left: 0, right: 200 }} />
移动端抽屉、滑块可用;桌面仍以点击为主,别为炫技强加 drag。
性能与 a11y
- 优先动画 transform / opacity,少动
width/height(或配合layout) - 尊重
prefers-reduced-motion:
const shouldReduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
transition={{ duration: shouldReduce ? 0 : 0.3 }}