React Spring和Framer Motion是React生态中最主流的两个动画库,但两者的设计哲学完全不同。React Spring把动画交给物理弹簧模型去计算,动画效果更贴近真实世界的运动规律;Framer Motion则走声明式组件路线,把动画、手势、布局变换整合成一套完整的工具链。选型时如果不了解它们的底层差异,很容易在项目后期才发现方案不匹配。本文将从原理、API设计、手势、编排、性能等多个角度展开对比,并给出选型建议。

一、底层原理:物理弹簧 vs 补间插值
React Spring的核心是物理弹簧模型。它不依赖duration和贝塞尔曲线,而是用刚度(stiffness)、阻尼(damping)和质量(mass)三个物理参数描述动画过程。动画的每一帧位置都由物理方程实时计算得出,这意味着动画可以被随时打断并从当前位置平滑恢复,不会出现跳帧或突兀的回退。这种可中断的特性是它区别于传统CSS动画和补间动画库的本质特征。
Framer Motion虽然也支持spring类型的动画,但它的基础模型是时间驱动的补间插值。你给一个目标值和时长(或弹簧配置),库内部通过requestAnimationFrame逐帧插值。它的优势在于动画类型更丰富:tween、spring、inertia、keyframes一应俱全,还能基于滚动位置(useScroll)驱动动画,这是React Spring早期版本不太方便实现的能力。
简单来说,React Spring是物理优先,动画中断和恢复是天然的;Framer Motion是功能优先,物理只是它支持的众多动画类型之一。理解了这一点,后面的API差异就很好解释了。
二、API风格对比:Hooks函数式 vs 声明式组件
React Spring提供的是一套Hooks风格的API,核心是useSpring、useSprings、useTrail、useTransition等钩子。你需要自己管理动画状态和触发时机,代码更像在写逻辑而不是写配置。
import { useSpring, animated } from '@react-spring/web'
function Counter() {
const [props, api] = useSpring(() => ({
scale: 1,
config: { tension: 220, friction: 20 }
}))
return (
<animated.div
style={props}
onMouseEnter={() => api.start({ scale: 1.2 })}
onMouseLeave={() => api.start({ scale: 1 })}
>
悬停放大
</animated.div>
)
}注意返回的必须是animated.div这类增强组件,普通的div不会响应动画值。这种方式灵活度高,适合和复杂业务逻辑深度结合,比如根据拖拽距离、滚动进度动态计算动画参数。
Framer Motion则采用声明式组件API,通过motion.div上的animate、initial、exit、transition等props描述动画状态,状态切换由React渲染驱动,配合AnimatePresence处理组件卸载时的离场动画,写起来非常直观。
import { motion, AnimatePresence } from 'framer-motion'
function Modal({ visible, onClose }) {
return (
<AnimatePresence>
{visible && (
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
transition={{ type: 'spring', stiffness: 300, damping: 25 }}
className="modal"
>
<button onClick={onClose}>关闭</button>
</motion.div>
)}
</AnimatePresence>
)
}两段代码实现的效果接近,但心智模型不同:React Spring需要你主动调用api.start触发动画,Framer Motion只需要改变传入animate的值。对于以状态流转为主的UI动画,Framer Motion的写法更省事;对于交互密集、需要精细控制的场景,React Spring的命令式入口更可控。
三、手势与动画编排能力
手势方面Framer Motion有明显优势。它内置了drag、whileHover、whileTap等能力,拖拽时可以直接给animate传入拖拽点坐标实现跟手效果,还支持dragConstraints限制拖拽范围、dragElastic控制边界弹性,几行代码就能做出类似原生App的滑动卡片。
<motion.div
drag="x"
dragConstraints={{ left: -200, right: 200 }}
dragElastic={0.2}
whileDrag={{ scale: 1.05, cursor: 'grabbing' }}
animate={{ x: 0 }}
/>React Spring做拖拽需要配合useDrag(来自@use-gesture/react)手动桥接,代码量略多,但好处是手势逻辑完全由你掌控,可以组合出更复杂的多指、多点交互。两个库其实都能实现跟手动画,差别在于Framer Motion开箱即用,React Spring需要自己组装。
编排能力上两者各有侧重。React Spring的useTrail可以轻松实现列表元素的stagger交错动画,useTransition擅长处理进出场切换和路由转场;Framer Motion则提供variants机制,通过嵌套的变体定义让父子组件自动按顺序级联动画,再配合delayChildren、staggerChildren参数,编排表达力很强。如果项目里有大量一组元素按节奏依次入场的需求,Framer Motion的variants写法通常更简洁。
四、性能、体积与选型建议
性能方面,两个库都基于requestAnimationFrame驱动,都会在动画期间跳过React渲染、直接操作样式,性能差距在日常场景中并不明显。React Spring的优势是在高频交互(如持续拖拽)下完全物理驱动,帧与帧之间不依赖React调度,抖动更小;Framer Motion的layout动画(layout prop)会自动用FLIP技术处理布局变化,做列表重排、元素换位动画时几乎是降维打击,这是React Spring没有直接对应的功能。
包体积上,React Spring核心(@react-spring/web)gzip后大约20KB出头,Framer Motion gzip后约35KB到45KB(取决于版本和是否拆分入口)。如果是对首屏体积敏感的移动端项目,React Spring更轻;如果已经大量使用其手势和布局动画能力,Framer Motion多出的体积完全物有所值。
选型建议可以归纳为:做交互驱动的微交互、拖拽物理效果、对体积敏感的项目,优先React Spring;做UI状态流转动画、模态框进出场、路由转场、列表布局动画、需要快速出效果的中后台或营销页面,优先Framer Motion。两者也可以共存,例如整体用Framer Motion处理转场,个别复杂交互用React Spring实现。动画库没有绝对优劣,关键是让库的设计哲学和你的动画需求对上号。
React SpringFramer MotionReact动画修改时间:2026-09-05 08:36:45