motion/react
motion/react
简介
motion/react 是一个现代化的 React 动画库,提供声明式 API 来创建流畅、高性能的动画效果。
主要特性
- 声明式动画:通过简洁的 props 定义动画状态,无需编写复杂的动画逻辑。
- 高性能渲染:充分利用硬件加速和优化的渲染策略,确保动画丝滑流畅。
- 无缝集成:与 React 生态完美融合,原生支持 TypeScript。
- 丰富交互:内置拖拽、悬停、点击等多种交互手势的动画响应。
工作原理
传统 React 动画库通常依赖 React 的 diff 更新机制来实现元素属性变化,每次动画帧都需要经过 React 的渲染周期,这在复杂动画场景下容易造成性能瓶颈。
motion/react 在 React 内部实现了一套独立的动画更新系统:
| 特性 | 说明 |
|---|---|
| 绕过渲染周期 | 直接操作 DOM 元素的视觉属性,不触发 React 重新渲染 |
| 硬件加速 | 专注于 transform、opacity 等可硬件加速的属性 |
| 流畅性能 | 避免 React 协调过程带来的帧率损耗 |
💡 理解这一机制是熟练使用 motion/react 的关键——它决定了为什么某些写法性能更好,以及如何合理组织动画代码。
基础用法
1. 状态动画
motion/react 提供了 initial、animate、transition 等属性来定义元素的动画状态。
import * as motion from 'motion/react-client';
export default function EnterAnimation() {
return (
<motion.div
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
transition={{
duration: 0.4,
scale: { type: 'spring', visualDuration: 0.4, bounce: 0.5 },
}}
style={ball}
/>
)
}
const ball = {
width: 100,
height: 100,
backgroundColor: '#dd00ee',
borderRadius: '50%',
}
2. 交互式动画
motion/react 支持多种交互事件,如悬停、点击、拖拽等。
import * as motion from 'motion/react-client'
export default function Gestures() {
return <motion.div whileHover={{ scale: 1.2 }} whileTap={{ scale: 0.8 }} style={box} />
}
const box = {
width: 100,
height: 100,
backgroundColor: '#9911ff',
borderRadius: 5,
}
3. 拖拽功能
motion/react 提供了强大的拖拽功能,可以轻松实现可拖拽元素。
import { motion, useMotionValue, useMotionValueEvent } from "motion/react";
import { useState } from "react";
export default function DraggableCard() {
const x = useMotionValue(0);
const y = useMotionValue(0);
const [coords, setCoords] = useState({ x: 0, y: 0 });
// 订阅 motion value 的变化
useMotionValueEvent(x, "change", (latest) => {
setCoords((prev) => ({ ...prev, x: latest }));
});
useMotionValueEvent(y, "change", (latest) => {
setCoords((prev) => ({ ...prev, y: latest }));
});
return (
<div style={{ height: "100vh", display: "flex", justifyContent: "center", alignItems: "center" }}>
<motion.div
drag
style={{
width: 100,
height: 100,
backgroundColor: "skyblue",
borderRadius: 20,
x, // 绑定 motion value
y,
}}
/>
<div style={{ marginLeft: 20 }}>
<p>X: {coords.x.toFixed(0)}</p>
<p>Y: {coords.y.toFixed(0)}</p>
</div>
</div>
);
}
4. 转换动画
motion/react 支持各种变换动画,如旋转、平移、缩放等。
import * as motion from 'motion/react-client'
export default function Rotate() {
return <motion.div style={box} animate={{ rotate: 360 }} transition={{ duration: 1 }} />
}
const box = {
width: 100,
height: 100,
backgroundColor: '#ff0088',
borderRadius: 5,
}
5. 共享布局动画
motion/react 提供了 layoutId 属性来实现共享布局动画,这在标签切换、列表重排等场景中非常有用。
import { motion } from 'motion/react'
import { useState } from 'react'
const tabs = ['首页', '产品', '关于']
export default function TabSwitch() {
const [activeTab, setActiveTab] = useState(0)
return (
<div style={{ display: 'flex', gap: 4, padding: 4, background: '#f0f0f0', borderRadius: 8 }}>
{tabs.map((tab, index) => (
<button
key={tab}
onClick={() => setActiveTab(index)}
style={{
position: 'relative',
padding: '8px 16px',
border: 'none',
background: 'transparent',
cursor: 'pointer',
fontSize: 14,
}}
>
{tab}
{activeTab === index && (
<motion.div
layoutId="activeTab"
style={{
position: 'absolute',
inset: 0,
background: 'white',
borderRadius: 6,
zIndex: -1,
boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
}}
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
/>
)}
</button>
))}
</div>
)
}
高级特性
1. Motion Values
Motion Values 是 motion/react 中的核心概念,它们是响应式的数值,可以在动画过程中被监听和修改。
const x = useMotionValue(0);
const y = useMotionValue(0);
// 监听值的变化
useMotionValueEvent(x, "change", (latest) => {
console.log("X value changed:", latest);
});
2. 自定义过渡效果
motion/react 支持多种过渡类型,包括弹簧、惯性、关键帧等。
<motion.div
animate={{ x: 100 }}
transition={{
type: "spring",
stiffness: 100,
damping: 10
}}
/>
3. 动画序列
可以使用 key 属性和 AnimatePresence 组件来创建动画序列。
<AnimatePresence mode="wait">
<motion.div
key={currentStep}
initial={{ opacity: 0, x: 100 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -100 }}
>
Step {currentStep}
</motion.div>
</AnimatePresence>
性能优化
1. 优先使用 transform 和 opacity
motion/react 的动画系统对 transform 和 opacity 做了特殊优化,这些属性不会触发布局重排和重绘,能够充分利用 GPU 加速。
// ✅ 推荐:使用 transform 和 opacity
<motion.div
animate={{ x: 100, opacity: 0.5, scale: 1.2 }}
/>
// ❌ 避免:动画化会触发重排的属性
<motion.div
animate={{ width: 200, height: 200, left: 100 }}
/>
2. 使用 layout 属性优化布局动画
当需要动画化布局属性(如宽高、位置)时,使用 layout 属性让 motion/react 自动处理布局变化,避免手动计算。
// ✅ 使用 layout 属性处理布局变化
<motion.div
layout
style={{ width: isExpanded ? 300 : 100 }}
transition={{ type: "spring", stiffness: 300, damping: 30 }}
/>
3. 合理使用 will-change
对于复杂的动画,可以通过 will-change 提示浏览器提前优化:
<motion.div
animate={{ x: 100 }}
style={{ willChange: "transform" }}
/>
4. 避免不必要的 Motion Values 更新
使用 useTransform 和 useSpring 创建派生值,避免在渲染周期中直接修改 Motion Values:
import { useMotionValue, useTransform, useSpring } from "motion/react";
function SmoothAnimation() {
const x = useMotionValue(0);
// ✅ 使用 useSpring 创建平滑的派生值
const smoothX = useSpring(x, { stiffness: 300, damping: 30 });
// ✅ 使用 useTransform 映射值范围
const opacity = useTransform(x, [0, 100], [1, 0]);
return <motion.div style={{ x: smoothX, opacity }} />;
}
5. 懒加载动画组件
对于不在首屏的动画,可以使用动态导入减少初始加载开销:
import { lazy, Suspense } from "react";
const HeavyAnimation = lazy(() => import("./HeavyAnimation"));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyAnimation />
</Suspense>
);
}
6. 使用 reducedMotion
尊重用户的系统偏好,为不喜欢动画的用户提供降级体验:
import { useReducedMotion } from "motion/react";
function AccessibleAnimation() {
const shouldReduceMotion = useReducedMotion();
return (
<motion.div
animate={shouldReduceMotion ? {} : { x: 100, opacity: 0.5 }}
transition={{ duration: shouldReduceMotion ? 0 : 0.3 }}
/>
);
}
总结
motion/react 是一个功能强大且易于使用的 React 动画库,它提供了简洁的 API 来创建高性能的动画效果。通过合理使用其提供的各种功能,开发者可以轻松地为应用添加流畅的交互动画,提升用户体验。
无论是基础的进入/退出动画,还是复杂的拖拽和共享布局动画,motion/react 都能提供优雅的解决方案。随着 Web 应用对用户体验要求的不断提高,掌握 motion/react 将成为前端开发者的必备技能之一。
