Framer Motion入门指南 🚀
为什么选择Framer Motion?
在React应用中实现流畅的动画效果一直是一个挑战。虽然我们可以使用CSS动画或其他动画库,但Framer Motion凭借其声明式API和强大的功能,让动画实现变得异常简单。✨
主要优势:
- 声明式API,符合React理念
- 内置手势支持
- 自动处理动画中断和过渡
- 支持SVG动画
- 完善的TypeScript支持
快速开始
安装
npm install framer-motion
# 或
yarn add framer-motion核心概念
1. motion组件
motion是Framer Motion的核心概念。它可以让任何HTML或SVG元素具备动画能力:
import { motion } from "framer-motion"
// 支持所有HTML元素
<motion.div />
<motion.span />
<motion.img />
// 支持SVG元素
<motion.svg />
<motion.path />
<motion.circle />对需要添加动画的标签用motion.[标签]替换原始标签。
2. 基础动画属性
animate
常规对象写法
animate这个属性,最基本的用法就是传入一个对象,比如,我们最常变化的几个属性 👉 x、y、scale、rotate还有opacity。
注意:x,y会默认添加translateZ(0px),可能会导致动画失效,注意排查或者使用常规写法
function BasicAnimation() {
return (
<motion.div
animate={{
x: 100,
y: 50,
scale: 1.2,
rotate: 180
}}
>
动画元素
</motion.div>
)
}关键帧配置写法
animate中的值可以设置为一系列关键帧。这将按顺序通过每个值设置动画。
<motion.div
animate={{ x: [0, 100, 0] }}
transition={{ duration: 3, times: [0, 0.2, 1] }}
/>我们可以通过将值的当前状态设置为null来使用它作为初始关键帧。这样,如果一个关键帧动画中断了另一个动画,过渡将感觉更自然。
style
motion元素上的style属性,这个属性一般元素上也有,但是motion元素上的style属性也是被强化了的,比如,我们曾在animate传入的👉x、y、scale、rotate,可以直接在style里面书写。
<motion.div
style={{
x: 100, // 向右移动100px
y: 200, // 向下移动200px
scale: 0.5, // 缩放至0.5倍
rotate: 45, // 旋转45度
}}
>
</motion.div>initial
如果我们要设置一个元素的初始状态,除了可以通过style,也可以通过initial。如果你使用initial,你甚至有些时候可以不用去用style属性了。
但是有一种情况,initial和style必须一起使用,就是当你需要给initial传false的时候。
当你给initial传递false,同时通过animate设置了一个变化数据的对象,那么你的这个初始动画就不会生效,它会没有动画,直接在加载后显示成你animate传入的对象的状态。
function FadeIn() {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1 }}
>
渐入效果
</motion.div>
)
}transition
控制动画的过渡效果:
| 属性 | 说明 | 可选值/类型 |
|---|---|---|
| type | 动画类型 | "tween"(补间动画), "spring"(弹性动画), "inertia"(惯性动画) |
| duration | 动画持续时间 | 数字(秒) |
| delay | 动画延迟时间 | 数字(秒) |
| ease | 缓动函数 | "linear", "easeIn", "easeOut", "easeInOut", [0.42, 0, 0.58, 1] |
| repeat | 重复次数 | 数字, Infinity |
| repeatType | 重复类型 | "loop"(循环), "reverse"(往返), "mirror"(镜像) |
当type为"spring"时的特有属性:
| 属性 | 说明 | 推荐值范围 |
|---|---|---|
| stiffness | 弹簧刚度 | 100-200 |
| damping | 阻尼 | 10-20 |
| mass | 质量 | 1-5 |
| bounce | 弹性 | 0-1 |
示例代码:
function CustomTransition() {
return (
<motion.div
animate={{ x: 100 }}
transition={{
type: "spring",
stiffness: 100,
damping: 15,
mass: 1,
delay: 0.2,
repeat: Infinity,
repeatType: "reverse"
}}
>
弹性动画
</motion.div>
)
}Exit
在React中,当一个组件被移除时,它通常会立即被移除。Motion提供了AnimatePresence组件,该组件在元素执行退出动画时将其保留在DOM中。
<AnimatePresence>
{isVisible && (
<motion.div
key="modal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>Animating CSS variables
有时,可以方便地使用CSS变量来控制多个元素的动画。
<motion.ul
initial={{ '--rotate': '0deg' }}
animate={{ '--rotate': '360deg' }}
transition={{ duration: 2, repeat: Infinity }}
>
<li style={{ width: '100px', height: '100px', backgroundColor: 'red', transform: 'rotate(var(--rotate))' }} />
<li style={{ width: '100px', height: '100px', backgroundColor: 'blue', transform: 'rotate(var(--rotate))' }} />
<li style={{ width: '100px', height: '100px', backgroundColor: 'green', transform: 'rotate(var(--rotate))' }} />
</motion.ul>实用动画示例
1. 悬浮效果
function HoverCard() {
return (
<motion.div
whileHover={{
scale: 1.05,
boxShadow: "0px 5px 15px rgba(0,0,0,0.1)"
}}
transition={{ duration: 0.3 }}
style={{
padding: "20px",
background: "white",
borderRadius: "10px"
}}
>
悬浮查看效果
</motion.div>
)
}2. 点击效果
function ClickButton() {
return (
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
transition={{ type: "spring", stiffness: 400, damping: 17 }}
>
点击我
</motion.button>
)
}3. 列表动画
function AnimatedList() {
const items = ["项目1", "项目2", "项目3"]
return (
<div>
{items.map((item, index) => (
<motion.div
key={item}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.5,
delay: index * 0.2
}}
>
{item}
</motion.div>
))}
</div>
)
}动画变体(Variants)
变体允许我们预定义动画状态,使代码更加清晰和可维护:
const cardVariants = {
hidden: {
opacity: 0,
y: 20
},
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.5
}
}
}
function Card() {
return (
<motion.div
variants={cardVariants}
initial="hidden"
animate="visible"
>
使用变体的卡片
</motion.div>
)
}默认情况下,此子动画将与父动画同时启动。但是通过变体,我们可以访问新的过渡道具,如when,delayChildren,staggerChildren和staggerDirection。
when: 用于控制子动画相对于父动画的启动时间。- "beforeChildren": 父动画在子动画之前启动。
- "afterChildren": 父动画在子动画之后启动。
const parentVariant = {
animate: {
opacity: 1,
transition: {
when: "beforeChildren", // 父动画先于子动画启动
},
},
};delayChildren: 用于在父动画启动后延迟子动画的启动时间。
const parentVariant = {
animate: {
opacity: 1,
transition: {
delayChildren: 0.5, // 子动画延迟0.5秒启动
},
},
};staggerChildren: 用于在子动画之间添加间隔时间,使它们依次启动。
const parentVariant = {
animate: {
opacity: 1,
transition: {
staggerChildren: 0.2, // 每个子动画之间间隔0.2秒
},
},
};staggerDirection: 用于控制子动画的启动顺序。 (1: 正向顺序(默认)。)
const parentVariant = {
animate: {
opacity: 1,
transition: {
staggerChildren: 0.2,
staggerDirection: -1, // 子动画以反向顺序启动
},
},
};动态变量
每个变量都可以定义为一个函数,该函数在变量被激活时进行解析。
const variants = {
hidden: { opacity: 0 },
visible: (index) => ({
opacity: 1,
transition: { delay: index * 0.3 }
})
}Animation controls 动画控件
有时我们需要手动控制动画播放。可以使用useAnimate钩子。
function MyComponent() {
const [scope, animate] = useAnimate()
useEffect(() => {
const controls = animate([
[scope.current, { x: "100%" }],
["li", { opacity: 1 }]
])
controls.speed = 0.8
return () => controls.stop()
}, [])
return (
<ul ref={scope}>
<li />
<li />
<li />
</ul>
)
}Animate content 动画内容
通过将MotionValue作为motion组件的子组件传递,它将在HTML中呈现其最新值。
import { useMotionValue, motion, animate } from "motion/react"
function Counter() {
const count = useMotionValue(0)
useEffect(() => {
const controls = animate(count, 100, { duration: 5 })
return () => controls.stop()
}, [])
return <motion.pre>{count}</motion.pre>
}实战练习:制作一个动画按钮组件
function AnimatedButton({ children, onClick }) {
return (
<motion.button
onClick={onClick}
whileHover={{
scale: 1.05,
backgroundColor: "#0056b3"
}}
whileTap={{ scale: 0.95 }}
initial={{
backgroundColor: "#007bff",
color: "white",
border: "none",
padding: "10px 20px",
borderRadius: "5px"
}}
transition={{
duration: 0.2,
ease: "easeInOut"
}}
>
{children}
</motion.button>
)
}小结
本文介绍了Framer Motion的基础用法,包括:
- motion组件的基本使用
- 核心动画属性
- 实用动画示例
- 变体的使用