Framer Motion Variants 属性详解

·
次浏览
AI 摘要生成中

Variants 属性在 Framer Motion 中是一种定义可重用动画状态的方式。它们允许您创建复杂的、协调的动画,而无需在每个组件上单独定义动画属性。

基本概念

Variants 是一个对象,其中包含了命名的动画状态。每个状态可以定义元素的样式和动画属性。

基本用法

  1. 定义 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 },
};
  1. 使用 Variants
<motion.div variants={variants} initial="hidden" animate="visible" />
<motion.div variants={variants} initial="hidden" animate="visible" />

高级特性

  1. 动态 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"
/>
  1. 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>
  );
}
  1. 循环动画

使用 repeatrepeatType 创建循环动画:

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",
    },
  },
};
  1. 条件 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"} />;
}
  1. 组合 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 来创建一个错开的动画效果,而每个项目则有自己的弹簧动画。

这种方法的优势在于:

  1. 动画逻辑与组件逻辑分离
  2. 可以轻松地在不同组件间重用动画
  3. 通过父子关系自动协调复杂的动画序列

通过掌握 variants,您可以创建出复杂、协调且可重用的动画,大大提高动画开发的效率和灵活性。