这三个库都用于在 React 应用中实现动画效果,但底层原理和适用场景截然不同。framer-motion 主打声明式 API 和强大的布局动画能力,适合快速构建复杂交互;react-spring 基于物理弹簧模型,提供极高的性能和对动画值的精细控制,适合追求自然物理感的场景;react-transition-group 则是底层的 CSS 类名切换工具,适合简单的进入/退出过渡或遗留项目维护。
在 React 生态中,framer-motion、react-spring 和 react-transition-group 是解决动画问题的三大主流方案。它们都能让界面动起来,但底层实现哲学完全不同。作为架构师,我们需要理解它们在性能、开发体验和适用边界上的差异,才能做出正确的技术选型。
framer-motion 采用声明式 API,你只需定义动画的“目标状态”。
animate 属性驱动。// framer-motion: 声明目标状态
import { motion } from "framer-motion";
function Box() {
return <motion.div animate={{ x: 100, opacity: 1 }} />;
}
react-spring 基于物理弹簧模型,动画是数值的连续变化。
useSpring 钩子返回动画值。// react-spring: 物理弹簧模型
import { useSpring, animated } from "@react-spring/web";
function Box() {
const props = useSpring({ x: 100, opacity: 1 });
return <animated.div style={props} />;
}
react-transition-group 不处理动画逻辑,只管理 CSS 类名的切换时机。
// react-transition-group: CSS 类名切换
import { CSSTransition } from "react-transition-group";
function Box({ show }) {
return (
<CSSTransition in={show} timeout={300} classNames="fade">
<div className="box" /> {/* 需在 CSS 中定义 .fade-enter-active 等 */}
</CSSTransition>
);
}
framer-motion 内置了强大的手势系统。
drag、whileHover、whileTap 等属性。// framer-motion: 内置拖拽与点击反馈
<motion.div
drag
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
/>
react-spring 需要手动结合事件监听器。
useDrag 等辅助钩子(通常在 @react-spring/web 或独立包中)。// react-spring: 手动结合事件
import { useSpring, useChain, useTrail } from "@react-spring/web";
// 通常需配合 @use-gesture/react 使用
const { x } = useSpring({ x: isDragging ? mouseX : 0 });
return <animated.div style={{ x }} onMouseDown={handleDrag} />;
react-transition-group 几乎没有交互支持。
// react-transition-group: 无内置交互
// 需手动管理 state 来触发 transition
const [active, setActive] = useState(false);
return (
<CSSTransition in={active} timeout={300} classNames="item">
<div onClick={() => setActive(!active)} />
</CSSTransition>
);
framer-motion 拥有业界领先的布局动画能力。
layoutId 即可实现元素位置变化的平滑过渡。// framer-motion: 自动布局过渡
<motion.div layoutId="card" onClick={() => setSelected(id)} />
// 点击后,另一个具有相同 layoutId 的元素会自动过渡过来
react-spring 需要手动计算位置变化。
// react-spring: 手动计算位置
const { x } = useSpring({ x: isOpen ? 100 : 0 });
// 开发者需自行计算 100 这个值从哪里来
return <animated.div style={{ transform: x.to(v => `translateX(${v}px)`) }} />;
react-transition-group 不支持布局动画。
// react-transition-group: 仅控制显隐
<CSSTransition in={isVisible} timeout={300} classNames="slide">
<div /> {/* 位置变化需靠 CSS 或父容器布局 */}
</CSSTransition>
framer-motion 优化了 GPU 加速。
x, y, scale 等属性转换为 transform。// framer-motion: 自动优化 transform
<motion.div animate={{ x: 100 }} /> {/* 内部转为 translate3d */}
react-spring 提供最高的数值控制粒度。
// react-spring: 精细数值控制
const { opacity } = useSpring({ opacity: scrollY.to(v => 1 - v / 100) });
// 适合将滚动位置映射为透明度
react-transition-group 依赖浏览器 CSS 引擎。
// react-transition-group: 纯 CSS 驱动
/* CSS */
.fade-enter-active { transition: opacity 300ms; }
/* JS 只负责加类名,动画由浏览器处理 */
尽管实现方式不同,这三个库都遵循 React 的核心设计模式。
// 所有库都支持在 JSX 中直接定义动画行为
// FM: <motion.div />
// Spring: <animated.div />
// RTG: <CSSTransition>
// FM: AnimatePresence
<AnimatePresence><motion.div /></AnimatePresence>
// Spring: useTransition
const transitions = useTransition(items, { from: ..., enter: ..., leave: ... })
// RTG: TransitionGroup
<TransitionGroup><CSSTransition /></TransitionGroup>
// 都可以读取外部 state 来驱动动画
const isOpen = useStore(state => state.isOpen);
// 所有库都接受 isOpen 作为 prop 或依赖项
| 特性 | framer-motion | react-spring | react-transition-group |
|---|---|---|---|
| 核心原理 | 声明式状态驱动 | 物理弹簧模型 | CSS 类名生命周期 |
| API 风格 | 组件属性 (animate) | Hooks (useSpring) | 组件包装器 (CSSTransition) |
| 布局动画 | ✅ 自动 (layoutId) | ❌ 需手动计算 | ❌ 不支持 |
| 手势交互 | ✅ 内置 (drag, hover) | ⚠️ 需配合库 | ❌ 无 |
| 性能优化 | 自动 GPU 加速 | 高频数值插值 | 纯 CSS 引擎 |
| 学习曲线 | 低 | 中 | 低 (但 CSS 需额外写) |
| 维护状态 | 活跃维护 | 活跃维护 | 维护中 (偏底层) |
framer-motion 像是现代 UI 开发的瑞士军刀 🇨🇭 —— 功能全面,开箱即用。如果你的团队需要快速交付高质量的交互界面,尤其是涉及列表排序、共享元素过渡或复杂手势,它是首选。它能显著减少动画相关的样板代码。
react-spring 像是精密的物理实验室 🧪 —— 适合对动画质感有极致追求的场景。如果你在做数据可视化、游戏化界面,或者需要动画值与业务逻辑深度绑定(如滚动驱动动画),它的物理模型能提供更自然的反馈。
react-transition-group 像是基础的螺丝刀 🪛 —— 简单、可靠,但功能有限。除非你只需要简单的淡入淡出,或者正在维护大量依赖 CSS 过渡的旧代码,否则在新项目中建议优先考虑前两者。它更适合作为底层原语,而非应用层的主要动画方案。
最终结论:对于大多数现代 React 应用,framer-motion 提供了最佳的开发体验与功能平衡。只有在特定的物理动画需求或极简 CSS 场景下,才考虑另外两者。
选择 framer-motion 如果你需要快速实现复杂的交互动画、布局切换或手势支持。它的声明式 API 学习曲线低,且内置了丰富的功能(如 drag, layoutId),适合大多数现代 React 应用,尤其是需要高保真原型或复杂 UI 反馈的项目。
选择 react-spring 如果你需要基于物理原理的自然动画效果,或者需要对动画数值进行精细的插值控制。它在处理高频更新(如跟随鼠标)时性能极佳,适合数据可视化、游戏化交互或对动画曲线有严格要求的场景。
选择 react-transition-group 如果你只需要简单的 CSS 类名切换(如淡入淡出),或者正在维护依赖 CSS 过渡的遗留代码。它没有 JS 动画开销,适合极简场景,但在新项目中通常建议优先考虑更强大的替代方案。
npm install motion
Motion is available for React, JavaScript and Vue.
import { motion } from "motion/react"
function Component() {
return <motion.div animate={{ x: 100 }} />
}
Get started with Motion for React.
Note: Framer Motion is now Motion. Import from motion/react instead of framer-motion.
import { animate } from "motion"
animate("#box", { x: 100 })
Get started with JavaScript.
<script>
import { motion } from "motion-v"
</script>
<template> <motion.div :animate={{ x: 100 }} /> </template>
Get started with Motion for Vue.
Browse 330+ official examples, with copy-paste code that'll level-up your animations whether you're a beginner or an expert.
Over 100 examples come with a full step-by-step tutorial.
A one-time payment, lifetime-updates membership:
Motion is sustainable thanks to the kind support of its sponsors.
Motion powers the animations for all websites built with Framer, the web builder for creative pros. The Motion website itself is built on Framer, for its delightful canvas-based editing and powerful CMS features.
Motion drives the animations on the Cursor homepage, and is working with Cursor to bring powerful AI workflows to the Motion examples and docs.