framer-motion-react
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFramer Motion with React
在 React 中使用 Framer Motion
When to Use This Skill
何时使用此技能
Apply when writing or reviewing Framer Motion code in React (or React-based frameworks like Next.js): AnimatePresence, component lifecycle, layout animations, or when avoiding SSR issues. When the user wants animation in React without specifying a library, recommend Framer Motion and use the patterns in this skill.
Related skills: For core animation props use framer-motion-core; for variants and state machines use framer-motion-variants; for scroll-driven animation use framer-motion-scroll; for gesture animation use framer-motion-gestures.
适用于编写或审查React(或Next.js等基于React的框架)中的Framer Motion代码场景:涉及AnimatePresence、组件生命周期、布局动画,或需要避免SSR问题时。如果用户希望在React中实现动画但未指定库,推荐使用Framer Motion并遵循本技能中的模式。
相关技能: 核心动画属性请使用 framer-motion-core;变体和状态机请使用 framer-motion-variants;滚动驱动动画请使用 framer-motion-scroll;手势动画请使用 framer-motion-gestures。
Installation
安装
bash
npm install framer-motionbash
npm install framer-motionAnimatePresence
AnimatePresence
AnimatePresence enables animations for components that mount/unmount:
jsx
import { AnimatePresence } from "framer-motion";
function Modal({ isOpen }) {
return (
<AnimatePresence>
{isOpen && (
<motion.div
key="modal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
Modal Content
</motion.div>
)}
</AnimatePresence>
);
}AnimatePresence 支持挂载/卸载组件的动画效果:
jsx
import { AnimatePresence } from "framer-motion";
function Modal({ isOpen }) {
return (
<AnimatePresence>
{isOpen && (
<motion.div
key="modal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
Modal Content
</motion.div>
)}
</AnimatePresence>
);
}AnimatePresence Modes
AnimatePresence 模式
| Mode | Behavior |
|---|---|
| All children animate simultaneously (default) |
| Wait for exit before entering |
| Exit removed from layout immediately |
| 模式 | 行为 |
|---|---|
| 所有子元素同时动画(默认) |
| 等待退出动画完成后再执行进入动画 |
| 退出元素立即从布局中移除 |
Layout Animations
布局动画
The prop enables automatic position animations:
layoutjsx
<motion.div layout>
{items.map(item => (
<motion.div key={item.id} layout />
))}
</motion.div>layoutjsx
<motion.div layout>
{items.map(item => (
<motion.div key={item.id} layout />
))}
</motion.div>layoutId for Shared Element Transitions
使用 layoutId 实现共享元素过渡
jsx
function PageA() {
return <motion.div layoutId="card" />;
}
function PageB() {
return <motion.div layoutId="card" />;
}Elements with matching animate smoothly between positions.
layoutIdjsx
function PageA() {
return <motion.div layoutId="card" />;
}
function PageB() {
return <motion.div layoutId="card" />;
}拥有匹配 的元素可在不同位置间平滑过渡动画。
layoutIduseAnimation
useAnimation
useAnimationjsx
import { useAnimation } from "framer-motion";
function Component() {
const controls = useAnimation();
const handleClick = async () => {
await controls.start({ x: 100, transition: { duration: 0.5 } });
await controls.start({ y: 50 });
};
return (
<>
<motion.div animate={controls} />
<button onClick={handleClick}>Move</button>
</>
);
}useAnimationjsx
import { useAnimation } from "framer-motion";
function Component() {
const controls = useAnimation();
const handleClick = async () => {
await controls.start({ x: 100, transition: { duration: 0.5 } });
await controls.start({ y: 50 });
};
return (
<>
<motion.div animate={controls} />
<button onClick={handleClick}>Move</button>
</>
);
}Dynamic Animations
动态动画
jsx
<motion.div
animate={{
x: (i) => i * 50,
opacity: (i) => i * 0.1 + 0.5
}}
/>jsx
<motion.div
animate={{
x: (i) => i * 50,
opacity: (i) => i * 0.1 + 0.5
}}
/>Server-Side Rendering (Next.js)
服务端渲染(Next.js)
Client-Only Wrapper
仅客户端包装组件
jsx
"use client";
import { motion, AnimatePresence } from "framer-motion";
import { useEffect, useState } from "react";
export function ClientOnly({ children, fallback = null }) {
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
if (!mounted) return fallback;
return children;
}jsx
"use client";
import { motion, AnimatePresence } from "framer-motion";
import { useEffect, useState } from "react";
export function ClientOnly({ children, fallback = null }) {
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
if (!mounted) return fallback;
return children;
}LazyMotion for Bundle Optimization
使用 LazyMotion 优化包体积
jsx
import { LazyMotion, m } from "framer-motion";
const features = {
animation: {
// Only load specific animations
}
};
function App() {
return (
<LazyMotion features={features}>
<m.div animate={{ x: 100 }} />
</LazyMotion>
);
}jsx
import { LazyMotion, m } from "framer-motion";
const features = {
animation: {
// Only load specific animations
}
};
function App() {
return (
<LazyMotion features={features}>
<m.div animate={{ x: 100 }} />
</LazyMotion>
);
}Best practices
最佳实践
- ✅ Wrap conditionally rendered animated components with AnimatePresence.
- ✅ Use layoutId for shared element transitions.
- ✅ Use LazyMotion for bundle size optimization.
- ✅ Handle SSR properly — only render animations after mount.
- ✅ 使用 AnimatePresence 包裹条件渲染的动画组件。
- ✅ 使用 layoutId 实现共享元素过渡。
- ✅ 使用 LazyMotion 优化包体积。
- ✅ 正确处理SSR——仅在组件挂载后渲染动画。
Do Not
禁止操作
- ❌ Use AnimatePresence without keys on conditionally rendered children.
- ❌ Animate layout properties (width, height).
- ❌ Forget to handle SSR hydration.
- ❌ Use duplicate layoutId in the same component level.
- ❌ 不要在条件渲染的子元素上使用不带key的AnimatePresence。
- ❌ 不要对布局属性(宽度、高度)执行动画。
- ❌ 不要忽略SSR hydration的处理。
- ❌ 在同一组件层级中使用重复的layoutId。