Variants 属性在 Framer Motion 中是一种定义可重用动画状态的方式。它们允许您创建复杂的、协调的动画,而无需在每个组件上单独定义动画属性。
基本概念
Variants 是一个对象,其中包含了命名的动画状态。每个状态可以定义元素的样式和动画属性。
基本用法
- 定义 Variants
const variants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
};
const variants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
};
- 使用 Variants
<motion.div variants={variants} initial="hidden" animate="visible" />
<motion.div variants={variants} initial="hidden" animate="visible" />
高级特性
- 动态 Variants
Variants 可以是函数,接收自定义参数:
const variants = {
hidden: { opacity: 0, y: 20 },
visible: (custom) => ({
opacity: 1,
y: 0,
transition: { delay: custom * 0.2 }
})
}
<motion.div
variants={variants}
custom={index} // 传递索引作为自定义参数
initial="hidden"
animate="visible"
/>
const variants = {
hidden: { opacity: 0, y: 20 },
visible: (custom) => ({
opacity: 1,
y: 0,
transition: { delay: custom * 0.2 }
})
}
<motion.div
variants={variants}
custom={index} // 传递索引作为自定义参数
initial="hidden"
animate="visible"
/>
- Variant 传播
父元素的 variant 会自动传播到子元素:
const list = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
when: "beforeChildren",
staggerChildren: 0.3,
},
},
};
const item = {
hidden: { y: 20, opacity: 0 },
visible: { y: 0, opacity: 1 },
};
function List() {
return (
<motion.ul variants={list} initial="hidden" animate="visible">
{items.map((item) => (
<motion.li key={item} variants={item} />
))}
</motion.ul>
);
}
const list = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
when: "beforeChildren",
staggerChildren: 0.3,
},
},
};
const item = {
hidden: { y: 20, opacity: 0 },
visible: { y: 0, opacity: 1 },
};
function List() {
return (
<motion.ul variants={list} initial="hidden" animate="visible">
{items.map((item) => (
<motion.li key={item} variants={item} />
))}
</motion.ul>
);
}
- 循环动画
使用 repeat
和 repeatType
创建循环动画:
const variants = {
animate: {
scale: [1, 1.2, 1],
transition: {
duration: 2,
repeat: Infinity,
repeatType: "reverse",
},
},
};
const variants = {
animate: {
scale: [1, 1.2, 1],
transition: {
duration: 2,
repeat: Infinity,
repeatType: "reverse",
},
},
};
- 条件 Variants
基于条件选择不同的 variant:
function Box({ isOn }) {
const variants = {
on: { backgroundColor: "#f00" },
off: { backgroundColor: "#ccc" },
};
return <motion.div variants={variants} animate={isOn ? "on" : "off"} />;
}
function Box({ isOn }) {
const variants = {
on: { backgroundColor: "#f00" },
off: { backgroundColor: "#ccc" },
};
return <motion.div variants={variants} animate={isOn ? "on" : "off"} />;
}
- 组合 Variants
可以组合多个 variants 来创建复杂的动画序列:
const button = {
tap: { scale: 0.9 },
hover: { scale: 1.1 },
initial: { scale: 1 }
}
<motion.button
variants={button}
initial="initial"
whileHover="hover"
whileTap="tap"
>
Click me
</motion.button>
const button = {
tap: { scale: 0.9 },
hover: { scale: 1.1 },
initial: { scale: 1 }
}
<motion.button
variants={button}
initial="initial"
whileHover="hover"
whileTap="tap"
>
Click me
</motion.button>
实际应用示例
让我们看一个更复杂的例子,展示如何使用 variants 创建一个动画列表:
import { motion } from "motion/react";
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1,
},
},
};
const item = {
hidden: { opacity: 0, y: 50 },
show: {
opacity: 1,
y: 0,
transition: {
type: "spring",
stiffness: 300,
damping: 24,
},
},
};
function AnimatedList({ items }) {
return (
<motion.ul variants={container} initial="hidden" animate="show">
{items.map((text, index) => (
<motion.li key={index} variants={item}>
{text}
</motion.li>
))}
</motion.ul>
);
}
import { motion } from "motion/react";
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1,
},
},
};
const item = {
hidden: { opacity: 0, y: 50 },
show: {
opacity: 1,
y: 0,
transition: {
type: "spring",
stiffness: 300,
damping: 24,
},
},
};
function AnimatedList({ items }) {
return (
<motion.ul variants={container} initial="hidden" animate="show">
{items.map((text, index) => (
<motion.li key={index} variants={item}>
{text}
</motion.li>
))}
</motion.ul>
);
}
在这个例子中,我们定义了两个 variants:一个用于容器(container
),一个用于每个列表项(item
)。容器 variant 使用 staggerChildren
来创建一个错开的动画效果,而每个项目则有自己的弹簧动画。
这种方法的优势在于:
- 动画逻辑与组件逻辑分离
- 可以轻松地在不同组件间重用动画
- 通过父子关系自动协调复杂的动画序列
通过掌握 variants,您可以创建出复杂、协调且可重用的动画,大大提高动画开发的效率和灵活性。