文档

文档

React

motion

motion

motion 组件驱动了 Motion for React 中的大多数动画。

每个 HTML 和 SVG 元素都有一个 motion 组件,例如 motion.divmotion.circle 等。可以将其视为一个普通的 React 组件,但它经过了增强,可实现 120fps 的动画和手势。

用法

从 Motion 导入 motion

// React
import { motion } from "motion/react"

// React Server Components
import * as motion from "motion/react-client"

现在你可以像使用任何普通 HTML/SVG 组件一样使用它

<motion.div className="box" />

但你还可以访问强大的动画 API,例如 animatelayoutwhileInView 属性等等。

<motion.div
  className="box"
  // Animate when this value changes:
  animate={{ scale: 2 }}
  // Fade in when the element enters the viewport:
  whileInView={{ opacity: 1 }}
  // Animate the component when its layout changes:
  layout
  // Style now supports indepedent transforms:
  style={{ x: 100 }}
/>

查看动画指南以获得 Motion for React 中动画的完整概述。

性能

motion 组件在 React 渲染周期之外为值设置动画,以提高性能。

使用motion 值而不是 React 状态来更新 style 也可以避免重新渲染。

const x = useMotionValue(0)

useEffect(() => {
  // Won't trigger a re-render!
  const timeout = setTimeout(() => x.set(100), 1000)

  return () => clearTimeout(timeout)
}, [])

return <motion.div style={{ x }} />

服务器端渲染

motion 组件与服务器端渲染完全兼容,这意味着组件的初始状态将反映在服务器生成的输出中。

// Server will output `translateX(100px)`
<motion.div initial={false} animate={{ x: 100 }} />

但这不包括某些需要 DOM 测量才能计算的 SVG 属性,例如 transform

自定义组件

任何 React 组件都可以通过将其作为函数传递给 motion.create() 来增强为 motion 组件。

const MotionComponent = motion.create(Component)

React 18 用户必须使用 forwardRef 来包裹他们的组件,并将 ref 传递给他们想要动画的元素

const Component = React.forwardRef((props, ref) => {
  return <div ref={ref} />
})

React 19 用户可以传递一个普通组件,并且 ref 将通过 props 传入

const Component = (props) => {
  return <div ref={props.ref} />
})

重要提示:请确保不要在另一个 React 渲染函数中调用 motion.create()!这将使每次渲染都创建一个新组件,从而破坏你的动画。

也可以将字符串传递给 motion.create,这将创建自定义 DOM 元素。

// Will render <custom-element /> into HTML
const MotionComponent = motion.create('custom-element')

默认情况下,所有 motion 属性(如 animate 等)都会从转发到所提供组件的 props 中过滤掉。通过提供 forwardMotionProps 配置,所提供的组件将接收这些属性。

motion.create(Component, { forwardMotionProps: true })

属性

motion 组件接受以下属性。

动画

initial

motion 组件的初始视觉状态。

可以设置为动画目标

<motion.section initial={{ opacity: 0, x: 0 }} />

Variants

<motion.li initial="visible" />
<motion.div initial={["visible", "active"]} />

或者设置为 false 以禁用进入动画,并最初渲染为在 animate 中找到的值。

<motion.div initial={false} animate={{ opacity: 0 }} />

animate

进入和更新时要动画到的目标。

可以设置为动画目标

<motion.div
  initial={{ boxShadow: "0px 0px #000" }}
  animate={{ boxShadow: "10px 10px #000" }}
/>

或 variants

<motion.li animate="visible" />
<motion.div initial="hidden" animate={["visible", "active"]} />

exit

当组件从树中移除时要动画到的目标。可以设置为动画目标或 variant。

注意:由于 React 的限制,要移除的组件必须AnimatePresence直接子级才能启用此动画。

<AnimatePresence>
  {isVisible && (
    <ul key="list">
      <motion.li exit={{ opacity: 0 }} />
    </ul>
  )}
</AnimatePresence>

transition

当动画属性(animatewhileHover 等)没有定义 transition 时,此组件要使用的默认过渡。

<motion.div transition={{ type: "spring" }} animate={{ scale: 1.2 }} />

variants

此组件的variants

const variants = {
  active: {
      backgroundColor: "#f00"
  },
  inactive: {
    backgroundColor: "#fff",
    transition: { duration: 2 }
  }
}

return (
  <motion.div
    variants={variants}
    animate={isActive ? "active" : "inactive"}
  />
)

style

普通的 React DOM style 属性,增加了对以下内容的支持motion 值和独立变换。

const x = useMotionValue(30)

return <motion.div style={{ x, rotate: 90, originX: 0.5 }} />

onUpdate

每当 motion 组件上的任何值更新时,都会触发回调。它提供一个参数,其中包含最新的值。

<motion.article
  animate={{ opacity: 1 }}
  onUpdate={latest => console.log(latest.opacity)}
/>

onAnimationStart

当任何动画(布局动画除外,请参阅 onLayoutAnimationStart)开始时触发的回调。

它提供一个参数,其中包含已启动动画的目标或 variant 名称。

<motion.circle
  animate={{ r: 10 }}
  onAnimationStart={latest => console.log(latest.r)}
/>

onAnimationComplete

当任何动画(布局动画除外,请参阅 onLayoutAnimationComplete)完成时触发的回调。

它提供一个参数,其中包含已完成动画的目标或 variant 名称。

<motion.circle
  animate={{ r: 10 }}
  onAnimationComplete={latest => console.log(latest.r)}
/>

Hover

whileHover

当 hover 手势处于活动状态时要标记的目标或 variants。

// As target
<motion.button whileHover={{ scale: 1.2 }} />
// As variants
<motion.div whileHover="hovered" />

onHoverStart

当指针开始悬停在组件上时触发的回调函数。提供触发的 PointerEvent

<motion.div onHoverStart={(event) => console.log(event)} />

onHoverEnd

当指针停止悬停在组件上时触发的回调函数。提供触发的 PointerEvent

<motion.div onHoverEnd={(event) => console.log(event)} />

Tap

whileTap

当 tap 手势处于活动状态时要标记的目标或 variants。

// As target
<motion.button whileTap={{ scale: 0.9 }} />
// As variants
<motion.div whileTap="tapped" />

onTapStart

当指针开始按下组件时触发的回调函数。提供触发的 PointerEvent

<motion.div onTapStart={(event) => console.log(event)} />

onTap

当指针停止按下组件并且指针在组件内部释放时触发的回调函数。提供触发的 PointerEvent

<motion.div onTap={(event) => console.log(event)} />

onTapCancel

当指针停止按下组件并且指针在组件外部释放时触发的回调函数。提供触发的 PointerEvent

<motion.div onTapCancel={(event) => console.log(event)} />

Focus

whileFocus

当 focus 手势处于活动状态时要标记的目标或 variants。

// As target
<motion.button whileFocus={{ outline: "dashed #000" }} />
// As variants
<motion.div whileFocus="focused" />

Pan

onPan

当在此元素上识别出 pan 手势时触发的回调函数。

注意:为了使 pan 手势能够与触摸输入正确配合使用,元素需要禁用 x/y 轴或两个轴上的触摸滚动,使用 touch-action CSS 规则。

function onPan(event, info) {
  console.log(info.point.x, info.point.y)
}

<motion.div onPan={onPan} />

Pan 和 drag 事件会提供原始 PointerEvent 以及一个包含以下 xy 点值的 info 对象

  • point:相对于设备或页面。

  • delta:自上次事件以来的距离。

  • offset:与原始事件的距离。

  • velocity:指针的当前速度。

onPanStart

当 pan 手势开始时触发的回调函数。提供触发的 PointerEventinfo

<motion.div onPanStart={(event, info) => console.log(info.delta.x)} />

onPanEnd

当 pan 手势结束时触发的回调函数。提供触发的 PointerEventinfo

<motion.div onPanEnd={(event, info) => console.log(info.delta.x)} />

Drag

drag

默认值:false

为此元素启用拖动。设置为 true 以在两个方向上拖动。设置为 "x""y" 以仅在特定方向上拖动。

<motion.div drag />

whileDrag

当 drag 手势处于活动状态时要标记的目标或 variants。

// As target
<motion.div drag whileDrag={{ scale: 0.9 }} />
// As variants
<motion.div drag whileDrag="dragging" />

dragConstraints

对可拖动区域应用约束。

设置为可选的 topleftrightbottom 值的对象,以像素为单位测量

<motion.div
  drag="x"
  dragConstraints={{ left: 0, right: 300 }}
/>

或作为另一个元素的 ref,以使用其边界框作为可拖动约束

const MyComponent = () => {
  const constraintsRef = useRef(null)

  return (
     <motion.div ref={constraintsRef}>
         <motion.div drag dragConstraints={constraintsRef} />
     </motion.div>
  )
}

dragSnapToOrigin

默认值:false

如果为 true,则可拖动元素在释放时将动画返回到其中心/原点。

<motion.div drag dragSnapToOrigin />

dragElastic

默认值:0.5

约束外允许的移动程度。0 = 不移动,1 = 完全移动。

默认设置为 0.5。也可以设置为 false 以禁用移动。

通过传递 top/right/bottom/left 的对象,可以为每个约束设置单独的值。任何缺失的值都将设置为 0

<motion.div
  drag
  dragConstraints={{ left: 0, right: 300 }}
  dragElastic={0.2}
/>

dragMomentum

默认值:true

在拖动结束时,将 pan 手势的动量应用于组件。默认设置为 true

<motion.div
  drag
  dragConstraints={{ left: 0, right: 300 }}
  dragMomentum={false}
/>

dragTransition

允许你更改拖动动量过渡。释放可拖动元素时,会启动类型为 "inertia" 的动画。动画基于你的拖动速度。此属性允许你自定义它。

<motion.div
  drag
  dragTransition={{ bounceStiffness: 600, bounceDamping: 10 }}
/>

dragDirectionLock

默认值:false

将拖动方向锁定为最早检测到的方向。例如,如果在 drag 手势开始之前,组件在 x 轴上的移动多于 y 轴,则在手势的剩余时间内将x 轴上拖动。

<motion.div drag dragDirectionLock />

dragPropagation

默认值: false

允许将 drag 手势传播到子组件。

<motion.div drag="x" dragPropagation />

dragControls

通常,拖动是通过按下组件并移动它来启动的。对于某些用例,例如单击视频 scrub bar 上的任意点,我们可能希望从与可拖动组件不同的组件启动拖动。

通过使用 useDragControls 创建 dragControlshook,我们可以将其传递到可拖动组件的 dragControls 属性中。它公开了一个 start 方法,可以从其他组件上的指针事件开始拖动。

const dragControls = useDragControls()

function startDrag(event) {
  dragControls.start(event, { snapToCursor: true })
}

return (
  <>
    <div onPointerDown={startDrag} />
    <motion.div drag="x" dragControls={dragControls} />
  </>
)

注意:鉴于通过设置 dragControls 你正在控制启动 drag 手势,因此可以通过设置 dragListener={false} 来禁用可拖动元素作为启动器。

dragListener

确定是否从事件侦听器触发 drag 手势。如果传递 dragControls,则将此设置为 false 将确保拖动只能由控件启动,而不是由可拖动元素上的 pointerdown 事件启动。

onDrag

当在此元素上识别出 drag 手势时触发的回调函数。

function onDrag(event, info) {
  console.log(info.point.x, info.point.y)
}

<motion.div drag onDrag={onDrag} />

Pan 和 drag 事件会提供原始 PointerEvent 以及一个包含以下 xy 点值的 info 对象

  • point:相对于设备或页面。

  • delta:自上次事件以来的距离。

  • offset:与原始事件的距离。

  • velocity:指针的当前速度。

onDragStart

当 drag 手势开始时触发的回调函数。提供触发的 PointerEventinfo

<motion.div drag onDragStart={(event, info) => console.log(info.delta.x)} />

onDragEnd

当 drag 手势结束时触发的回调函数。提供触发的 PointerEventinfo

<motion.div drag onDragEnd={(event, info) => console.log(info.delta.x)} />

onDirectionLock

当 drag 方向被确定时触发的回调函数。

<motion.div
  drag
  dragDirectionLock
  onDirectionLock={axis => console.log(axis)}
/>

Viewport

whileInView

当元素在视图中时要标记的目标或 variants。

// As target
<motion.div whileInView={{ opacity: 1 }} />
// As variants
<motion.div whileInView="visible" />

viewport

用于定义如何在视口中跟踪元素的可选配置。

<motion.section
  whileInView={{ opacity: 1 }}
  viewport={{ once: true }}
/>

可用选项

  • once:如果为 true,则元素一旦进入视口,将不再检测后续的离开/进入事件。

  • root:祖先可滚动元素的 ref,用于检测与其的交叉(而不是 window)。

  • margin:添加到视口的边距,以更改检测区域。默认为 "0px"。使用多个值来调整上/右/下/左,例如 "0px -20px 0px 100px"

  • amount:应进入视口以被视为“已进入”的元素量。可以是 "some""all" 或介于 01 之间的数字。默认为 "some"

onViewportEnter

当元素进入视口时触发的回调函数。提供 IntersectionObserverEntry,其中包含交叉事件的详细信息。

<motion.div onViewportEnter={(entry) => console.log(entry.isIntersecting)} />

onViewportLeave

当元素进入视口时触发的回调函数。提供 IntersectionObserverEntry,其中包含交叉事件的详细信息。

<motion.div onViewportLeave={(entry) => console.log(entry.intersectionRect)} />

布局

layout

默认值:false

如果为 true,则此组件将为其布局更改设置动画.

<motion.div layout />

如果设置为 "position""size",则分别只为其位置或大小设置动画。

<motion.img layout="position" />

layoutId

如果设置,此组件将为其布局更改设置动画。此外,当新元素进入 DOM 并且已存在具有匹配 layoutId 的元素时,它将从先前元素的大小/位置动画移出。

{items.map(item => (
   <motion.li layout>
      {item.name}
      {item.isSelected && <motion.div layoutId="underline" />}
   </motion.li>
))}

如果先前的组件保留在树中,则这两个元素将交叉淡入淡出。

layoutDependency

默认情况下,每次渲染都会检测布局更改。为了减少测量并因此提高性能,你可以传递 layoutDependency 属性。仅当此值更改时才会进行测量。

<motion.nav layout layoutDependency={isOpen} />

layoutScroll

为了使布局动画在可滚动元素中正常工作,需要测量其滚动偏移量。出于性能原因,Framer Motion 不会测量每个祖先的滚动偏移量。将 layoutScroll 属性添加到应测量的元素。

<motion.div layoutScroll style={{ overflow: "scroll" }}>
  <motion.div layout />
</motion.div>

layoutRoot

为了使布局动画在 position: fixed 元素中正常工作,我们需要考虑页面滚动。添加 layoutRoot 以将元素标记为 position: fixed

<motion.div layoutRoot style={{ position: "fixed" }}>
  <motion.div layout />
</motion.div>

onLayoutAnimationStart

布局动画开始时要运行的回调。

onLayoutAnimationComplete

布局动画完成时要运行的回调。

高级

inherit

设置为 false 以防止组件继承或传播父级 variant 中的更改。

custom

要传递给动态 variants 的自定义数据。

const variants = {
  visible: (custom) => ({
    opacity: 1,
    transition: { delay: custom * 0.2 }
  })
}

return (
  <motion.ul animate="visible">
    <motion.li custom={0} variants={variants} />
    <motion.li custom={1} variants={variants} />
    <motion.li custom={2} variants={variants} />
  </motion.ul>
)

transformTemplate

默认情况下,变换按 translatescalerotateskew 的顺序应用。

要更改此顺序,可以将 transformTemplate 设置为一个函数,该函数接受最新的变换和生成的变换字符串,并返回一个新的变换字符串。

// Use the latest transform values
<motion.div
  style={{ x: 0, rotate: 180 }}
  transformTemplate={
    ({ x, rotate }) => `rotate(${rotate}deg) translateX(${x}px)`
  }
/>
// Or the generated transform string
<motion.div
  style={{ x: 0, rotate: 180 }}
  transformTemplate={
    (latest, generated) => `translate(-50%, -50%) ${generated}`
  }
/>

motion 组件驱动了 Motion for React 中的大多数动画。

每个 HTML 和 SVG 元素都有一个 motion 组件,例如 motion.divmotion.circle 等。可以将其视为一个普通的 React 组件,但它经过了增强,可实现 120fps 的动画和手势。

用法

从 Motion 导入 motion

// React
import { motion } from "motion/react"

// React Server Components
import * as motion from "motion/react-client"

现在你可以像使用任何普通 HTML/SVG 组件一样使用它

<motion.div className="box" />

但你还可以访问强大的动画 API,例如 animatelayoutwhileInView 属性等等。

<motion.div
  className="box"
  // Animate when this value changes:
  animate={{ scale: 2 }}
  // Fade in when the element enters the viewport:
  whileInView={{ opacity: 1 }}
  // Animate the component when its layout changes:
  layout
  // Style now supports indepedent transforms:
  style={{ x: 100 }}
/>

查看动画指南以获得 Motion for React 中动画的完整概述。

性能

motion 组件在 React 渲染周期之外为值设置动画,以提高性能。

使用motion 值而不是 React 状态来更新 style 也可以避免重新渲染。

const x = useMotionValue(0)

useEffect(() => {
  // Won't trigger a re-render!
  const timeout = setTimeout(() => x.set(100), 1000)

  return () => clearTimeout(timeout)
}, [])

return <motion.div style={{ x }} />

服务器端渲染

motion 组件与服务器端渲染完全兼容,这意味着组件的初始状态将反映在服务器生成的输出中。

// Server will output `translateX(100px)`
<motion.div initial={false} animate={{ x: 100 }} />

但这不包括某些需要 DOM 测量才能计算的 SVG 属性,例如 transform

自定义组件

任何 React 组件都可以通过将其作为函数传递给 motion.create() 来增强为 motion 组件。

const MotionComponent = motion.create(Component)

React 18 用户必须使用 forwardRef 来包裹他们的组件,并将 ref 传递给他们想要动画的元素

const Component = React.forwardRef((props, ref) => {
  return <div ref={ref} />
})

React 19 用户可以传递一个普通组件,并且 ref 将通过 props 传入

const Component = (props) => {
  return <div ref={props.ref} />
})

重要提示:请确保不要在另一个 React 渲染函数中调用 motion.create()!这将使每次渲染都创建一个新组件,从而破坏你的动画。

也可以将字符串传递给 motion.create,这将创建自定义 DOM 元素。

// Will render <custom-element /> into HTML
const MotionComponent = motion.create('custom-element')

默认情况下,所有 motion 属性(如 animate 等)都会从转发到所提供组件的 props 中过滤掉。通过提供 forwardMotionProps 配置,所提供的组件将接收这些属性。

motion.create(Component, { forwardMotionProps: true })

属性

motion 组件接受以下属性。

动画

initial

motion 组件的初始视觉状态。

可以设置为动画目标

<motion.section initial={{ opacity: 0, x: 0 }} />

Variants

<motion.li initial="visible" />
<motion.div initial={["visible", "active"]} />

或者设置为 false 以禁用进入动画,并最初渲染为在 animate 中找到的值。

<motion.div initial={false} animate={{ opacity: 0 }} />

animate

进入和更新时要动画到的目标。

可以设置为动画目标

<motion.div
  initial={{ boxShadow: "0px 0px #000" }}
  animate={{ boxShadow: "10px 10px #000" }}
/>

或 variants

<motion.li animate="visible" />
<motion.div initial="hidden" animate={["visible", "active"]} />

exit

当组件从树中移除时要动画到的目标。可以设置为动画目标或 variant。

注意:由于 React 的限制,要移除的组件必须AnimatePresence直接子级才能启用此动画。

<AnimatePresence>
  {isVisible && (
    <ul key="list">
      <motion.li exit={{ opacity: 0 }} />
    </ul>
  )}
</AnimatePresence>

transition

当动画属性(animatewhileHover 等)没有定义 transition 时,此组件要使用的默认过渡。

<motion.div transition={{ type: "spring" }} animate={{ scale: 1.2 }} />

variants

此组件的variants

const variants = {
  active: {
      backgroundColor: "#f00"
  },
  inactive: {
    backgroundColor: "#fff",
    transition: { duration: 2 }
  }
}

return (
  <motion.div
    variants={variants}
    animate={isActive ? "active" : "inactive"}
  />
)

style

普通的 React DOM style 属性,增加了对以下内容的支持motion 值和独立变换。

const x = useMotionValue(30)

return <motion.div style={{ x, rotate: 90, originX: 0.5 }} />

onUpdate

每当 motion 组件上的任何值更新时,都会触发回调。它提供一个参数,其中包含最新的值。

<motion.article
  animate={{ opacity: 1 }}
  onUpdate={latest => console.log(latest.opacity)}
/>

onAnimationStart

当任何动画(布局动画除外,请参阅 onLayoutAnimationStart)开始时触发的回调。

它提供一个参数,其中包含已启动动画的目标或 variant 名称。

<motion.circle
  animate={{ r: 10 }}
  onAnimationStart={latest => console.log(latest.r)}
/>

onAnimationComplete

当任何动画(布局动画除外,请参阅 onLayoutAnimationComplete)完成时触发的回调。

它提供一个参数,其中包含已完成动画的目标或 variant 名称。

<motion.circle
  animate={{ r: 10 }}
  onAnimationComplete={latest => console.log(latest.r)}
/>

Hover

whileHover

当 hover 手势处于活动状态时要标记的目标或 variants。

// As target
<motion.button whileHover={{ scale: 1.2 }} />
// As variants
<motion.div whileHover="hovered" />

onHoverStart

当指针开始悬停在组件上时触发的回调函数。提供触发的 PointerEvent

<motion.div onHoverStart={(event) => console.log(event)} />

onHoverEnd

当指针停止悬停在组件上时触发的回调函数。提供触发的 PointerEvent

<motion.div onHoverEnd={(event) => console.log(event)} />

Tap

whileTap

当 tap 手势处于活动状态时要标记的目标或 variants。

// As target
<motion.button whileTap={{ scale: 0.9 }} />
// As variants
<motion.div whileTap="tapped" />

onTapStart

当指针开始按下组件时触发的回调函数。提供触发的 PointerEvent

<motion.div onTapStart={(event) => console.log(event)} />

onTap

当指针停止按下组件并且指针在组件内部释放时触发的回调函数。提供触发的 PointerEvent

<motion.div onTap={(event) => console.log(event)} />

onTapCancel

当指针停止按下组件并且指针在组件外部释放时触发的回调函数。提供触发的 PointerEvent

<motion.div onTapCancel={(event) => console.log(event)} />

Focus

whileFocus

当 focus 手势处于活动状态时要标记的目标或 variants。

// As target
<motion.button whileFocus={{ outline: "dashed #000" }} />
// As variants
<motion.div whileFocus="focused" />

Pan

onPan

当在此元素上识别出 pan 手势时触发的回调函数。

注意:为了使 pan 手势能够与触摸输入正确配合使用,元素需要禁用 x/y 轴或两个轴上的触摸滚动,使用 touch-action CSS 规则。

function onPan(event, info) {
  console.log(info.point.x, info.point.y)
}

<motion.div onPan={onPan} />

Pan 和 drag 事件会提供原始 PointerEvent 以及一个包含以下 xy 点值的 info 对象

  • point:相对于设备或页面。

  • delta:自上次事件以来的距离。

  • offset:与原始事件的距离。

  • velocity:指针的当前速度。

onPanStart

当 pan 手势开始时触发的回调函数。提供触发的 PointerEventinfo

<motion.div onPanStart={(event, info) => console.log(info.delta.x)} />

onPanEnd

当 pan 手势结束时触发的回调函数。提供触发的 PointerEventinfo

<motion.div onPanEnd={(event, info) => console.log(info.delta.x)} />

Drag

drag

默认值:false

为此元素启用拖动。设置为 true 以在两个方向上拖动。设置为 "x""y" 以仅在特定方向上拖动。

<motion.div drag />

whileDrag

当 drag 手势处于活动状态时要标记的目标或 variants。

// As target
<motion.div drag whileDrag={{ scale: 0.9 }} />
// As variants
<motion.div drag whileDrag="dragging" />

dragConstraints

对可拖动区域应用约束。

设置为可选的 topleftrightbottom 值的对象,以像素为单位测量

<motion.div
  drag="x"
  dragConstraints={{ left: 0, right: 300 }}
/>

或作为另一个元素的 ref,以使用其边界框作为可拖动约束

const MyComponent = () => {
  const constraintsRef = useRef(null)

  return (
     <motion.div ref={constraintsRef}>
         <motion.div drag dragConstraints={constraintsRef} />
     </motion.div>
  )
}

dragSnapToOrigin

默认值:false

如果为 true,则可拖动元素在释放时将动画返回到其中心/原点。

<motion.div drag dragSnapToOrigin />

dragElastic

默认值:0.5

约束外允许的移动程度。0 = 不移动,1 = 完全移动。

默认设置为 0.5。也可以设置为 false 以禁用移动。

通过传递 top/right/bottom/left 的对象,可以为每个约束设置单独的值。任何缺失的值都将设置为 0

<motion.div
  drag
  dragConstraints={{ left: 0, right: 300 }}
  dragElastic={0.2}
/>

dragMomentum

默认值:true

在拖动结束时,将 pan 手势的动量应用于组件。默认设置为 true

<motion.div
  drag
  dragConstraints={{ left: 0, right: 300 }}
  dragMomentum={false}
/>

dragTransition

允许你更改拖动动量过渡。释放可拖动元素时,会启动类型为 "inertia" 的动画。动画基于你的拖动速度。此属性允许你自定义它。

<motion.div
  drag
  dragTransition={{ bounceStiffness: 600, bounceDamping: 10 }}
/>

dragDirectionLock

默认值:false

将拖动方向锁定为最早检测到的方向。例如,如果在 drag 手势开始之前,组件在 x 轴上的移动多于 y 轴,则在手势的剩余时间内将x 轴上拖动。

<motion.div drag dragDirectionLock />

dragPropagation

默认值: false

允许将 drag 手势传播到子组件。

<motion.div drag="x" dragPropagation />

dragControls

通常,拖动是通过按下组件并移动它来启动的。对于某些用例,例如单击视频 scrub bar 上的任意点,我们可能希望从与可拖动组件不同的组件启动拖动。

通过使用 useDragControls 创建 dragControlshook,我们可以将其传递到可拖动组件的 dragControls 属性中。它公开了一个 start 方法,可以从其他组件上的指针事件开始拖动。

const dragControls = useDragControls()

function startDrag(event) {
  dragControls.start(event, { snapToCursor: true })
}

return (
  <>
    <div onPointerDown={startDrag} />
    <motion.div drag="x" dragControls={dragControls} />
  </>
)

注意:鉴于通过设置 dragControls 你正在控制启动 drag 手势,因此可以通过设置 dragListener={false} 来禁用可拖动元素作为启动器。

dragListener

确定是否从事件侦听器触发 drag 手势。如果传递 dragControls,则将此设置为 false 将确保拖动只能由控件启动,而不是由可拖动元素上的 pointerdown 事件启动。

onDrag

当在此元素上识别出 drag 手势时触发的回调函数。

function onDrag(event, info) {
  console.log(info.point.x, info.point.y)
}

<motion.div drag onDrag={onDrag} />

Pan 和 drag 事件会提供原始 PointerEvent 以及一个包含以下 xy 点值的 info 对象

  • point:相对于设备或页面。

  • delta:自上次事件以来的距离。

  • offset:与原始事件的距离。

  • velocity:指针的当前速度。

onDragStart

当 drag 手势开始时触发的回调函数。提供触发的 PointerEventinfo

<motion.div drag onDragStart={(event, info) => console.log(info.delta.x)} />

onDragEnd

当 drag 手势结束时触发的回调函数。提供触发的 PointerEventinfo

<motion.div drag onDragEnd={(event, info) => console.log(info.delta.x)} />

onDirectionLock

当 drag 方向被确定时触发的回调函数。

<motion.div
  drag
  dragDirectionLock
  onDirectionLock={axis => console.log(axis)}
/>

Viewport

whileInView

当元素在视图中时要标记的目标或 variants。

// As target
<motion.div whileInView={{ opacity: 1 }} />
// As variants
<motion.div whileInView="visible" />

viewport

用于定义如何在视口中跟踪元素的可选配置。

<motion.section
  whileInView={{ opacity: 1 }}
  viewport={{ once: true }}
/>

可用选项

  • once:如果为 true,则元素一旦进入视口,将不再检测后续的离开/进入事件。

  • root:祖先可滚动元素的 ref,用于检测与其的交叉(而不是 window)。

  • margin:添加到视口的边距,以更改检测区域。默认为 "0px"。使用多个值来调整上/右/下/左,例如 "0px -20px 0px 100px"

  • amount:应进入视口以被视为“已进入”的元素量。可以是 "some""all" 或介于 01 之间的数字。默认为 "some"

onViewportEnter

当元素进入视口时触发的回调函数。提供 IntersectionObserverEntry,其中包含交叉事件的详细信息。

<motion.div onViewportEnter={(entry) => console.log(entry.isIntersecting)} />

onViewportLeave

当元素进入视口时触发的回调函数。提供 IntersectionObserverEntry,其中包含交叉事件的详细信息。

<motion.div onViewportLeave={(entry) => console.log(entry.intersectionRect)} />

布局

layout

默认值:false

如果为 true,则此组件将为其布局更改设置动画.

<motion.div layout />

如果设置为 "position""size",则分别只为其位置或大小设置动画。

<motion.img layout="position" />

layoutId

如果设置,此组件将为其布局更改设置动画。此外,当新元素进入 DOM 并且已存在具有匹配 layoutId 的元素时,它将从先前元素的大小/位置动画移出。

{items.map(item => (
   <motion.li layout>
      {item.name}
      {item.isSelected && <motion.div layoutId="underline" />}
   </motion.li>
))}

如果先前的组件保留在树中,则这两个元素将交叉淡入淡出。

layoutDependency

默认情况下,每次渲染都会检测布局更改。为了减少测量并因此提高性能,你可以传递 layoutDependency 属性。仅当此值更改时才会进行测量。

<motion.nav layout layoutDependency={isOpen} />

layoutScroll

为了使布局动画在可滚动元素中正常工作,需要测量其滚动偏移量。出于性能原因,Framer Motion 不会测量每个祖先的滚动偏移量。将 layoutScroll 属性添加到应测量的元素。

<motion.div layoutScroll style={{ overflow: "scroll" }}>
  <motion.div layout />
</motion.div>

layoutRoot

为了使布局动画在 position: fixed 元素中正常工作,我们需要考虑页面滚动。添加 layoutRoot 以将元素标记为 position: fixed

<motion.div layoutRoot style={{ position: "fixed" }}>
  <motion.div layout />
</motion.div>

onLayoutAnimationStart

布局动画开始时要运行的回调。

onLayoutAnimationComplete

布局动画完成时要运行的回调。

高级

inherit

设置为 false 以防止组件继承或传播父级 variant 中的更改。

custom

要传递给动态 variants 的自定义数据。

const variants = {
  visible: (custom) => ({
    opacity: 1,
    transition: { delay: custom * 0.2 }
  })
}

return (
  <motion.ul animate="visible">
    <motion.li custom={0} variants={variants} />
    <motion.li custom={1} variants={variants} />
    <motion.li custom={2} variants={variants} />
  </motion.ul>
)

transformTemplate

默认情况下,变换按 translatescalerotateskew 的顺序应用。

要更改此顺序,可以将 transformTemplate 设置为一个函数,该函数接受最新的变换和生成的变换字符串,并返回一个新的变换字符串。

// Use the latest transform values
<motion.div
  style={{ x: 0, rotate: 180 }}
  transformTemplate={
    ({ x, rotate }) => `rotate(${rotate}deg) translateX(${x}px)`
  }
/>
// Or the generated transform string
<motion.div
  style={{ x: 0, rotate: 180 }}
  transformTemplate={
    (latest, generated) => `translate(-50%, -50%) ${generated}`
  }
/>

motion 组件驱动了 Motion for React 中的大多数动画。

每个 HTML 和 SVG 元素都有一个 motion 组件,例如 motion.divmotion.circle 等。可以将其视为一个普通的 React 组件,但它经过了增强,可实现 120fps 的动画和手势。

用法

从 Motion 导入 motion

// React
import { motion } from "motion/react"

// React Server Components
import * as motion from "motion/react-client"

现在你可以像使用任何普通 HTML/SVG 组件一样使用它

<motion.div className="box" />

但你还可以访问强大的动画 API,例如 animatelayoutwhileInView 属性等等。

<motion.div
  className="box"
  // Animate when this value changes:
  animate={{ scale: 2 }}
  // Fade in when the element enters the viewport:
  whileInView={{ opacity: 1 }}
  // Animate the component when its layout changes:
  layout
  // Style now supports indepedent transforms:
  style={{ x: 100 }}
/>

查看动画指南以获得 Motion for React 中动画的完整概述。

性能

motion 组件在 React 渲染周期之外为值设置动画,以提高性能。

使用motion 值而不是 React 状态来更新 style 也可以避免重新渲染。

const x = useMotionValue(0)

useEffect(() => {
  // Won't trigger a re-render!
  const timeout = setTimeout(() => x.set(100), 1000)

  return () => clearTimeout(timeout)
}, [])

return <motion.div style={{ x }} />

服务器端渲染

motion 组件与服务器端渲染完全兼容,这意味着组件的初始状态将反映在服务器生成的输出中。

// Server will output `translateX(100px)`
<motion.div initial={false} animate={{ x: 100 }} />

但这不包括某些需要 DOM 测量才能计算的 SVG 属性,例如 transform

自定义组件

任何 React 组件都可以通过将其作为函数传递给 motion.create() 来增强为 motion 组件。

const MotionComponent = motion.create(Component)

React 18 用户必须使用 forwardRef 来包裹他们的组件,并将 ref 传递给他们想要动画的元素

const Component = React.forwardRef((props, ref) => {
  return <div ref={ref} />
})

React 19 用户可以传递一个普通组件,并且 ref 将通过 props 传入

const Component = (props) => {
  return <div ref={props.ref} />
})

重要提示:请确保不要在另一个 React 渲染函数中调用 motion.create()!这将使每次渲染都创建一个新组件,从而破坏你的动画。

也可以将字符串传递给 motion.create,这将创建自定义 DOM 元素。

// Will render <custom-element /> into HTML
const MotionComponent = motion.create('custom-element')

默认情况下,所有 motion 属性(如 animate 等)都会从转发到所提供组件的 props 中过滤掉。通过提供 forwardMotionProps 配置,所提供的组件将接收这些属性。

motion.create(Component, { forwardMotionProps: true })

属性

motion 组件接受以下属性。

动画

initial

motion 组件的初始视觉状态。

可以设置为动画目标

<motion.section initial={{ opacity: 0, x: 0 }} />

Variants

<motion.li initial="visible" />
<motion.div initial={["visible", "active"]} />

或者设置为 false 以禁用进入动画,并最初渲染为在 animate 中找到的值。

<motion.div initial={false} animate={{ opacity: 0 }} />

animate

进入和更新时要动画到的目标。

可以设置为动画目标

<motion.div
  initial={{ boxShadow: "0px 0px #000" }}
  animate={{ boxShadow: "10px 10px #000" }}
/>

或 variants

<motion.li animate="visible" />
<motion.div initial="hidden" animate={["visible", "active"]} />

exit

当组件从树中移除时要动画到的目标。可以设置为动画目标或 variant。

注意:由于 React 的限制,要移除的组件必须AnimatePresence直接子级才能启用此动画。

<AnimatePresence>
  {isVisible && (
    <ul key="list">
      <motion.li exit={{ opacity: 0 }} />
    </ul>
  )}
</AnimatePresence>

transition

当动画属性(animatewhileHover 等)没有定义 transition 时,此组件要使用的默认过渡。

<motion.div transition={{ type: "spring" }} animate={{ scale: 1.2 }} />

variants

此组件的variants

const variants = {
  active: {
      backgroundColor: "#f00"
  },
  inactive: {
    backgroundColor: "#fff",
    transition: { duration: 2 }
  }
}

return (
  <motion.div
    variants={variants}
    animate={isActive ? "active" : "inactive"}
  />
)

style

普通的 React DOM style 属性,增加了对以下内容的支持motion 值和独立变换。

const x = useMotionValue(30)

return <motion.div style={{ x, rotate: 90, originX: 0.5 }} />

onUpdate

每当 motion 组件上的任何值更新时,都会触发回调。它提供一个参数,其中包含最新的值。

<motion.article
  animate={{ opacity: 1 }}
  onUpdate={latest => console.log(latest.opacity)}
/>

onAnimationStart

当任何动画(布局动画除外,请参阅 onLayoutAnimationStart)开始时触发的回调。

它提供一个参数,其中包含已启动动画的目标或 variant 名称。

<motion.circle
  animate={{ r: 10 }}
  onAnimationStart={latest => console.log(latest.r)}
/>

onAnimationComplete

当任何动画(布局动画除外,请参阅 onLayoutAnimationComplete)完成时触发的回调。

它提供一个参数,其中包含已完成动画的目标或 variant 名称。

<motion.circle
  animate={{ r: 10 }}
  onAnimationComplete={latest => console.log(latest.r)}
/>

Hover

whileHover

当 hover 手势处于活动状态时要标记的目标或 variants。

// As target
<motion.button whileHover={{ scale: 1.2 }} />
// As variants
<motion.div whileHover="hovered" />

onHoverStart

当指针开始悬停在组件上时触发的回调函数。提供触发的 PointerEvent

<motion.div onHoverStart={(event) => console.log(event)} />

onHoverEnd

当指针停止悬停在组件上时触发的回调函数。提供触发的 PointerEvent

<motion.div onHoverEnd={(event) => console.log(event)} />

Tap

whileTap

当 tap 手势处于活动状态时要标记的目标或 variants。

// As target
<motion.button whileTap={{ scale: 0.9 }} />
// As variants
<motion.div whileTap="tapped" />

onTapStart

当指针开始按下组件时触发的回调函数。提供触发的 PointerEvent

<motion.div onTapStart={(event) => console.log(event)} />

onTap

当指针停止按下组件并且指针在组件内部释放时触发的回调函数。提供触发的 PointerEvent

<motion.div onTap={(event) => console.log(event)} />

onTapCancel

当指针停止按下组件并且指针在组件外部释放时触发的回调函数。提供触发的 PointerEvent

<motion.div onTapCancel={(event) => console.log(event)} />

Focus

whileFocus

当 focus 手势处于活动状态时要标记的目标或 variants。

// As target
<motion.button whileFocus={{ outline: "dashed #000" }} />
// As variants
<motion.div whileFocus="focused" />

Pan

onPan

当在此元素上识别出 pan 手势时触发的回调函数。

注意:为了使 pan 手势能够与触摸输入正确配合使用,元素需要禁用 x/y 轴或两个轴上的触摸滚动,使用 touch-action CSS 规则。

function onPan(event, info) {
  console.log(info.point.x, info.point.y)
}

<motion.div onPan={onPan} />

Pan 和 drag 事件会提供原始 PointerEvent 以及一个包含以下 xy 点值的 info 对象

  • point:相对于设备或页面。

  • delta:自上次事件以来的距离。

  • offset:与原始事件的距离。

  • velocity:指针的当前速度。

onPanStart

当 pan 手势开始时触发的回调函数。提供触发的 PointerEventinfo

<motion.div onPanStart={(event, info) => console.log(info.delta.x)} />

onPanEnd

当 pan 手势结束时触发的回调函数。提供触发的 PointerEventinfo

<motion.div onPanEnd={(event, info) => console.log(info.delta.x)} />

Drag

drag

默认值:false

为此元素启用拖动。设置为 true 以在两个方向上拖动。设置为 "x""y" 以仅在特定方向上拖动。

<motion.div drag />

whileDrag

当 drag 手势处于活动状态时要标记的目标或 variants。

// As target
<motion.div drag whileDrag={{ scale: 0.9 }} />
// As variants
<motion.div drag whileDrag="dragging" />

dragConstraints

对可拖动区域应用约束。

设置为可选的 topleftrightbottom 值的对象,以像素为单位测量

<motion.div
  drag="x"
  dragConstraints={{ left: 0, right: 300 }}
/>

或作为另一个元素的 ref,以使用其边界框作为可拖动约束

const MyComponent = () => {
  const constraintsRef = useRef(null)

  return (
     <motion.div ref={constraintsRef}>
         <motion.div drag dragConstraints={constraintsRef} />
     </motion.div>
  )
}

dragSnapToOrigin

默认值:false

如果为 true,则可拖动元素在释放时将动画返回到其中心/原点。

<motion.div drag dragSnapToOrigin />

dragElastic

默认值:0.5

约束外允许的移动程度。0 = 不移动,1 = 完全移动。

默认设置为 0.5。也可以设置为 false 以禁用移动。

通过传递 top/right/bottom/left 的对象,可以为每个约束设置单独的值。任何缺失的值都将设置为 0

<motion.div
  drag
  dragConstraints={{ left: 0, right: 300 }}
  dragElastic={0.2}
/>

dragMomentum

默认值:true

在拖动结束时,将 pan 手势的动量应用于组件。默认设置为 true

<motion.div
  drag
  dragConstraints={{ left: 0, right: 300 }}
  dragMomentum={false}
/>

dragTransition

允许你更改拖动动量过渡。释放可拖动元素时,会启动类型为 "inertia" 的动画。动画基于你的拖动速度。此属性允许你自定义它。

<motion.div
  drag
  dragTransition={{ bounceStiffness: 600, bounceDamping: 10 }}
/>

dragDirectionLock

默认值:false

将拖动方向锁定为最早检测到的方向。例如,如果在 drag 手势开始之前,组件在 x 轴上的移动多于 y 轴,则在手势的剩余时间内将x 轴上拖动。

<motion.div drag dragDirectionLock />

dragPropagation

默认值: false

允许将 drag 手势传播到子组件。

<motion.div drag="x" dragPropagation />

dragControls

通常,拖动是通过按下组件并移动它来启动的。对于某些用例,例如单击视频 scrub bar 上的任意点,我们可能希望从与可拖动组件不同的组件启动拖动。

通过使用 useDragControls 创建 dragControlshook,我们可以将其传递到可拖动组件的 dragControls 属性中。它公开了一个 start 方法,可以从其他组件上的指针事件开始拖动。

const dragControls = useDragControls()

function startDrag(event) {
  dragControls.start(event, { snapToCursor: true })
}

return (
  <>
    <div onPointerDown={startDrag} />
    <motion.div drag="x" dragControls={dragControls} />
  </>
)

注意:鉴于通过设置 dragControls 你正在控制启动 drag 手势,因此可以通过设置 dragListener={false} 来禁用可拖动元素作为启动器。

dragListener

确定是否从事件侦听器触发 drag 手势。如果传递 dragControls,则将此设置为 false 将确保拖动只能由控件启动,而不是由可拖动元素上的 pointerdown 事件启动。

onDrag

当在此元素上识别出 drag 手势时触发的回调函数。

function onDrag(event, info) {
  console.log(info.point.x, info.point.y)
}

<motion.div drag onDrag={onDrag} />

Pan 和 drag 事件会提供原始 PointerEvent 以及一个包含以下 xy 点值的 info 对象

  • point:相对于设备或页面。

  • delta:自上次事件以来的距离。

  • offset:与原始事件的距离。

  • velocity:指针的当前速度。

onDragStart

当 drag 手势开始时触发的回调函数。提供触发的 PointerEventinfo

<motion.div drag onDragStart={(event, info) => console.log(info.delta.x)} />

onDragEnd

当 drag 手势结束时触发的回调函数。提供触发的 PointerEventinfo

<motion.div drag onDragEnd={(event, info) => console.log(info.delta.x)} />

onDirectionLock

当 drag 方向被确定时触发的回调函数。

<motion.div
  drag
  dragDirectionLock
  onDirectionLock={axis => console.log(axis)}
/>

Viewport

whileInView

当元素在视图中时要标记的目标或 variants。

// As target
<motion.div whileInView={{ opacity: 1 }} />
// As variants
<motion.div whileInView="visible" />

viewport

用于定义如何在视口中跟踪元素的可选配置。

<motion.section
  whileInView={{ opacity: 1 }}
  viewport={{ once: true }}
/>

可用选项

  • once:如果为 true,则元素一旦进入视口,将不再检测后续的离开/进入事件。

  • root:祖先可滚动元素的 ref,用于检测与其的交叉(而不是 window)。

  • margin:添加到视口的边距,以更改检测区域。默认为 "0px"。使用多个值来调整上/右/下/左,例如 "0px -20px 0px 100px"

  • amount:应进入视口以被视为“已进入”的元素量。可以是 "some""all" 或介于 01 之间的数字。默认为 "some"

onViewportEnter

当元素进入视口时触发的回调函数。提供 IntersectionObserverEntry,其中包含交叉事件的详细信息。

<motion.div onViewportEnter={(entry) => console.log(entry.isIntersecting)} />

onViewportLeave

当元素进入视口时触发的回调函数。提供 IntersectionObserverEntry,其中包含交叉事件的详细信息。

<motion.div onViewportLeave={(entry) => console.log(entry.intersectionRect)} />

布局

layout

默认值:false

如果为 true,则此组件将为其布局更改设置动画.

<motion.div layout />

如果设置为 "position""size",则分别只为其位置或大小设置动画。

<motion.img layout="position" />

layoutId

如果设置,此组件将为其布局更改设置动画。此外,当新元素进入 DOM 并且已存在具有匹配 layoutId 的元素时,它将从先前元素的大小/位置动画移出。

{items.map(item => (
   <motion.li layout>
      {item.name}
      {item.isSelected && <motion.div layoutId="underline" />}
   </motion.li>
))}

如果先前的组件保留在树中,则这两个元素将交叉淡入淡出。

layoutDependency

默认情况下,每次渲染都会检测布局更改。为了减少测量并因此提高性能,你可以传递 layoutDependency 属性。仅当此值更改时才会进行测量。

<motion.nav layout layoutDependency={isOpen} />

layoutScroll

为了使布局动画在可滚动元素中正常工作,需要测量其滚动偏移量。出于性能原因,Framer Motion 不会测量每个祖先的滚动偏移量。将 layoutScroll 属性添加到应测量的元素。

<motion.div layoutScroll style={{ overflow: "scroll" }}>
  <motion.div layout />
</motion.div>

layoutRoot

为了使布局动画在 position: fixed 元素中正常工作,我们需要考虑页面滚动。添加 layoutRoot 以将元素标记为 position: fixed

<motion.div layoutRoot style={{ position: "fixed" }}>
  <motion.div layout />
</motion.div>

onLayoutAnimationStart

布局动画开始时要运行的回调。

onLayoutAnimationComplete

布局动画完成时要运行的回调。

高级

inherit

设置为 false 以防止组件继承或传播父级 variant 中的更改。

custom

要传递给动态 variants 的自定义数据。

const variants = {
  visible: (custom) => ({
    opacity: 1,
    transition: { delay: custom * 0.2 }
  })
}

return (
  <motion.ul animate="visible">
    <motion.li custom={0} variants={variants} />
    <motion.li custom={1} variants={variants} />
    <motion.li custom={2} variants={variants} />
  </motion.ul>
)

transformTemplate

默认情况下,变换按 translatescalerotateskew 的顺序应用。

要更改此顺序,可以将 transformTemplate 设置为一个函数,该函数接受最新的变换和生成的变换字符串,并返回一个新的变换字符串。

// Use the latest transform values
<motion.div
  style={{ x: 0, rotate: 180 }}
  transformTemplate={
    ({ x, rotate }) => `rotate(${rotate}deg) translateX(${x}px)`
  }
/>
// Or the generated transform string
<motion.div
  style={{ x: 0, rotate: 180 }}
  transformTemplate={
    (latest, generated) => `translate(-50%, -50%) ${generated}`
  }
/>

motion

示例

超越基础功能

Motion+是一次性付费,终身会员资格。

除了高级 Motion 功能、早期访问内容和私有 Discord 社区外,你还将解锁对 90 多个高级示例源代码的访问权限,这些示例将此页面上的 API 提升到一个新的水平。

加载中...
加载中...
保持关注

订阅以获取最新的新闻和更新。

保持关注

订阅以获取最新的新闻和更新。