Skip to content

Framer Motion入门指南 🚀 ​

为什么选择Framer Motion? ​

在React应用中实现流畅的动画效果一直是一个挑战。虽然我们可以使用CSS动画或其他动画库,但Framer Motion凭借其声明式API和强大的功能,让动画实现变得异常简单。✨

主要优势:

  • 声明式API,符合React理念
  • 内置手势支持
  • 自动处理动画中断和过渡
  • 支持SVG动画
  • 完善的TypeScript支持

快速开始 ​

安装 ​

bash
npm install framer-motion
# 或
yarn add framer-motion

核心概念 ​

1. motion组件 ​

motion是Framer Motion的核心概念。它可以让任何HTML或SVG元素具备动画能力:

jsx
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),可能会导致动画失效,注意排查或者使用常规写法

jsx
function BasicAnimation() {
  return (
    <motion.div
      animate={{
        x: 100,
        y: 50,
        scale: 1.2,
        rotate: 180
      }}
    >
      动画元素
    </motion.div>
  )
}
关键帧配置写法 ​

animate中的值可以设置为一系列关键帧。这将按顺序通过每个值设置动画。

jsx
<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里面书写。

jsx
<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传入的对象的状态。

jsx
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

示例代码:

jsx
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中。

jsx
<AnimatePresence>
  {isVisible && (
    <motion.div
      key="modal"
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    />
  )}
</AnimatePresence>

Animating CSS variables ​

有时,可以方便地使用CSS变量来控制多个元素的动画。

jsx
<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. 悬浮效果 ​

jsx
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. 点击效果 ​

jsx
function ClickButton() {
  return (
    <motion.button
      whileHover={{ scale: 1.1 }}
      whileTap={{ scale: 0.9 }} 
      transition={{ type: "spring", stiffness: 400, damping: 17 }}
    >
      点击我
    </motion.button>
  )
}

3. 列表动画 ​

jsx
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) ​

变体允许我们预定义动画状态,使代码更加清晰和可维护:

jsx
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": 父动画在子动画之后启动。
jsx
const parentVariant = {
  animate: {
    opacity: 1,
    transition: {
      when: "beforeChildren", // 父动画先于子动画启动
    },
  },
};
  • delayChildren: 用于在父动画启动后延迟子动画的启动时间。
jsx
const parentVariant = {
  animate: {
    opacity: 1,
    transition: {
      delayChildren: 0.5, // 子动画延迟0.5秒启动
    },
  },
};
  • staggerChildren: 用于在子动画之间添加间隔时间,使它们依次启动。
jsx
const parentVariant = {
  animate: {
    opacity: 1,
    transition: {
      staggerChildren: 0.2, // 每个子动画之间间隔0.2秒
    },
  },
};
  • staggerDirection: 用于控制子动画的启动顺序。 (1: 正向顺序(默认)。)
jsx
const parentVariant = {
  animate: {
    opacity: 1,
    transition: {
      staggerChildren: 0.2,
      staggerDirection: -1, // 子动画以反向顺序启动
    },
  },
};

动态变量 ​

每个变量都可以定义为一个函数,该函数在变量被激活时进行解析。

jsx
const variants = {
  hidden: { opacity: 0 },
  visible: (index) => ({
    opacity: 1,
    transition: { delay: index * 0.3 }
  })
}

Animation controls 动画控件 ​

有时我们需要手动控制动画播放。可以使用useAnimate钩子。

jsx
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中呈现其最新值。

jsx
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>
}

实战练习:制作一个动画按钮组件 ​

jsx
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组件的基本使用
  • 核心动画属性
  • 实用动画示例
  • 变体的使用

相关资源 ​