motion
motion
组件驱动了 Motion for React 中的大多数动画。
每个 HTML 和 SVG 元素都有一个 motion
组件,例如 motion.div
、motion.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,例如 animate
、layout
、whileInView
属性等等。
<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
当动画属性(animate
、whileHover
等)没有定义 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
以及一个包含以下 x
和 y
点值的 info
对象
point
:相对于设备或页面。delta
:自上次事件以来的距离。offset
:与原始事件的距离。velocity
:指针的当前速度。
onPanStart
当 pan 手势开始时触发的回调函数。提供触发的 PointerEvent
和 info
。
<motion.div onPanStart={(event, info) => console.log(info.delta.x)} />
onPanEnd
当 pan 手势结束时触发的回调函数。提供触发的 PointerEvent
和 info
。
<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
对可拖动区域应用约束。
设置为可选的 top
、left
、right
和 bottom
值的对象,以像素为单位测量
<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
创建 dragControls
hook,我们可以将其传递到可拖动组件的 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
以及一个包含以下 x
和 y
点值的 info
对象
point
:相对于设备或页面。delta
:自上次事件以来的距离。offset
:与原始事件的距离。velocity
:指针的当前速度。
onDragStart
当 drag 手势开始时触发的回调函数。提供触发的 PointerEvent
和 info
。
<motion.div drag onDragStart={(event, info) => console.log(info.delta.x)} />
onDragEnd
当 drag 手势结束时触发的回调函数。提供触发的 PointerEvent
和 info
。
<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"
或介于0
和1
之间的数字。默认为"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
默认情况下,变换按 translate
、scale
、rotate
和 skew
的顺序应用。
要更改此顺序,可以将 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.div
、motion.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,例如 animate
、layout
、whileInView
属性等等。
<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
当动画属性(animate
、whileHover
等)没有定义 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
以及一个包含以下 x
和 y
点值的 info
对象
point
:相对于设备或页面。delta
:自上次事件以来的距离。offset
:与原始事件的距离。velocity
:指针的当前速度。
onPanStart
当 pan 手势开始时触发的回调函数。提供触发的 PointerEvent
和 info
。
<motion.div onPanStart={(event, info) => console.log(info.delta.x)} />
onPanEnd
当 pan 手势结束时触发的回调函数。提供触发的 PointerEvent
和 info
。
<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
对可拖动区域应用约束。
设置为可选的 top
、left
、right
和 bottom
值的对象,以像素为单位测量
<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
创建 dragControls
hook,我们可以将其传递到可拖动组件的 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
以及一个包含以下 x
和 y
点值的 info
对象
point
:相对于设备或页面。delta
:自上次事件以来的距离。offset
:与原始事件的距离。velocity
:指针的当前速度。
onDragStart
当 drag 手势开始时触发的回调函数。提供触发的 PointerEvent
和 info
。
<motion.div drag onDragStart={(event, info) => console.log(info.delta.x)} />
onDragEnd
当 drag 手势结束时触发的回调函数。提供触发的 PointerEvent
和 info
。
<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"
或介于0
和1
之间的数字。默认为"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
默认情况下,变换按 translate
、scale
、rotate
和 skew
的顺序应用。
要更改此顺序,可以将 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.div
、motion.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,例如 animate
、layout
、whileInView
属性等等。
<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
当动画属性(animate
、whileHover
等)没有定义 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
以及一个包含以下 x
和 y
点值的 info
对象
point
:相对于设备或页面。delta
:自上次事件以来的距离。offset
:与原始事件的距离。velocity
:指针的当前速度。
onPanStart
当 pan 手势开始时触发的回调函数。提供触发的 PointerEvent
和 info
。
<motion.div onPanStart={(event, info) => console.log(info.delta.x)} />
onPanEnd
当 pan 手势结束时触发的回调函数。提供触发的 PointerEvent
和 info
。
<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
对可拖动区域应用约束。
设置为可选的 top
、left
、right
和 bottom
值的对象,以像素为单位测量
<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
创建 dragControls
hook,我们可以将其传递到可拖动组件的 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
以及一个包含以下 x
和 y
点值的 info
对象
point
:相对于设备或页面。delta
:自上次事件以来的距离。offset
:与原始事件的距离。velocity
:指针的当前速度。
onDragStart
当 drag 手势开始时触发的回调函数。提供触发的 PointerEvent
和 info
。
<motion.div drag onDragStart={(event, info) => console.log(info.delta.x)} />
onDragEnd
当 drag 手势结束时触发的回调函数。提供触发的 PointerEvent
和 info
。
<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"
或介于0
和1
之间的数字。默认为"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
默认情况下,变换按 translate
、scale
、rotate
和 skew
的顺序应用。
要更改此顺序,可以将 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}` } />