React 声明式动画实战:用 Framer Motion 接管进场、列表与手势
test12026-10-190 次阅读
为什么不直接写 CSS 动画
项目里弹窗、列表、卡片经常需要进场和退场动画。手写 CSS keyframes 要维护 class、监听 transitionend,列表项增删时还要自己算位置。Framer Motion 把这些变成声明式的 props:给元素加 initial、animate、exit,剩下的交给它。
基础进场与退场
用 motion.div 替代普通 div,声明初始态和动画目标态。配合 AnimatePresence 才能播放 exit 动画,否则卸载即消失。
import { motion, AnimatePresence } from "framer-motion";
import { useState } from "react";
export function Notice({ open }: { open: boolean }) {
return (
<AnimatePresence>
{open && (
<motion.div
initial={{ opacity: 0, y: -8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -8 }}
transition={{ duration: 0.2 }}
>
这是一条通知
</motion.div>
)}
</AnimatePresence>
);
}
列表 stagger 一个接一个出现
列表项逐个进场用 variants 的 staggerChildren,父容器控制节奏,子项只需声明 own 动作。
const container = {
hidden: { opacity: 0 },
show: { opacity: 1, transition: { staggerChildren: 0.06 } },
};
const item = {
hidden: { opacity: 0, y: 12 },
show: { opacity: 1, y: 0 },
};
export function CardList({ data }: { data: string[] }) {
return (
<motion.ul variants={container} initial="hidden" animate="show">
{data.map((t) => (
<motion.li key={t} variants={item}>{t}</motion.li>
))}
</motion.ul>
);
}
手势拖拽
drag 属性能让元素被鼠标或手指拖动,dragConstraints 限制范围,拖完自动回弹。
export function DraggableChip() {
return (
<motion.div
drag
dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }}
dragElastic={0.4}
whileDrag={{ scale: 1.1 }}
style={{ width: 80, height: 80, background: "#2563eb", borderRadius: 12 }}
/>
);
}
落地要点
- 只给会进场/退场的可见元素套 motion,纯静态结构用普通标签。
- AnimatePresence 必须包住条件渲染的那一层,且子元素要有稳定 key。
- 列表动画优先用 Layout 动画(layout 属性)处理重排,别手动算 transform。
- SSR 项目里 Framer Motion 是纯客户端库,弹窗组件记得标 "use client" 或只在客户端挂载。
T
test1
文章作者
为什么不直接写 CSS 动画 项目里弹窗、列表、卡片经常需要进场和退场动画。手写 CSS keyframes 要维护...
- 分类
- 技术
- 发布时间
- 2026-10-19
- 字数
- 约 1840 字
- 阅读
- 0 次
