# Gestures
A powerful gesture recognition system for the browser.Motion extends the basic set of event listeners provided by React with a simple yet powerful set of UI gesture recognisers.
It currently has support for hover, tap, pan and drag gesture detection. Each gesture has a series of event listeners that you can attach to your motion
component.
# Animation helpers
motion
components provide two helper props: whileHover
and whileTap
. These can define animation targets to temporarily animate to while a gesture is active.
<motion.button
whileHover={{
scale: 1.2,
transition: { duration: 1 },
}}
whileTap={{ scale: 0.9 }}
/>
Both props can be set either as a target of values to animate to, or the name of any variants defined via the variants
prop. Variants will flow down through children as normal.
<motion.button
whileTap="tap"
whileHover="hover"
variants={buttonVariants}
>
<svg>
<motion.path variants={iconVariants} />
</svg>
</motion.button>
motion
components automatically manage the interplay between these while
props. So for instance, if hovering starts and stops while the tap gesture is active, the tap gesture receives priority and any properties defined on both will remain in their tapped state.
Likewise, if both gestures are defined and tapping ends, the component will know to animate either to the state defined in whileHover
, or the component's original state, depending on whether tapping ends inside or outside of the component.
# A note on SVG filters
The while
helper properties won't work on SVG filter
components, as these elements don't have a physical presence and therefore don't receive events. To respond to gestures, you need to introduce React state to the component and attach listeners to the physical element.
const MyComponent = () => {
const [isHovered, setHovered] = useState(false)
// Simplified example
return (
<svg>
<image
filter="url(#blur)"
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
/>
<filter id="blur">
<motion.div
initial={false}
animate={{ stdDeviation: isHovered ? 0 : 2 }}
/>
</filter>
</svg>
)
}
# Hover
The hover gesture detects when a pointer hovers over or leaves a component.
It differs from onMouseEnter
and onMouseLeave
in that hover is guaranteed to only fire as a result of actual mouse events (as opposed to browser-generated mice events emulated from touch input).
<motion.a
whileHover={{ scale: 1.2 }}
onHoverStart={e => {}}
onHoverEnd={e => {}}
/>
# whileHover: VariantLabels | TargetAndTransition
Properties or variant label to animate to while the hover gesture is recognised.
<motion.div whileHover={{ scale: 1.2 }} />
# onHoverStart(event, info): void
Callback function that fires when pointer starts hovering over the component.
<motion.div onHoverStart={() => console.log('Hover starts')} />
event: MouseEvent |
---|
info: EventInfo |
# onHoverEnd(event, info): void
Callback function that fires when pointer stops hovering over the component.
<motion.div onHoverEnd={() => console.log("Hover ends")} />
event: MouseEvent |
---|
info: EventInfo |
# Tap
The tap gesture detects when a pointer presses down and releases on the same component.
It fires a tap
event when tapping successfully completes on an component, and a tapCancel
event when tapping ends outside the component.
If the tappable component is a child of a draggable component, it'll automatically cancel the tap gesture if the pointer moves further than 3 pixels during the gesture.
# whileTap: VariantLabels | TargetAndTransition
Properties or variant label to animate to while the component is pressed.
<motion.div whileTap={{ scale: 0.8 }} />
# onTap(event, info): void
Callback when the tap gesture successfully ends on this element.
function onTap(event, info) {
console.log(info.point.x, info.point.y)
}
<motion.div onTap={onTap} />
event: MouseEvent | TouchEvent | PointerEvent The originating pointer event. |
---|
info: TapInfo An |
# onTapStart(event, info): void
Callback when the tap gesture starts on this element.
function onTapStart(event, info) {
console.log(info.point.x, info.point.y)
}
<motion.div onTapStart={onTapStart} />
event: MouseEvent | TouchEvent | PointerEvent The originating pointer event. |
---|
info: TapInfo An |
# onTapCancel(event, info): void
Callback when the tap gesture ends outside this element.
function onTapCancel(event, info) {
console.log(info.point.x, info.point.y)
}
<motion.div onTapCancel={onTapCancel} />
event: MouseEvent | TouchEvent | PointerEvent The originating pointer event. |
---|
info: TapInfo An |
# Pan
The pan gesture recognises when a pointer presses down on a component and moves further than 3 pixels. The pan gesture is ended when the pointer is released.
<motion.div onPan={(e, pointInfo) => {}} />
# onPan(event, info): void
Callback function that fires when the pan gesture is recognised on this element.
function onPan(event, info) {
console.log(info.point.x, info.point.y)
}
<motion.div onPan={onPan} />
event: MouseEvent | TouchEvent | PointerEvent The originating pointer event. |
---|
info: PanInfo A
|
# onPanStart(event, info): void
Callback function that fires when the pan gesture begins on this element.
function onPanStart(event, info) {
console.log(info.point.x, info.point.y)
}
<motion.div onPanStart={onPanStart} />
event: MouseEvent | TouchEvent | PointerEvent The originating pointer event. |
---|
info: PanInfo A
|
# onPanEnd(event, info): void
Callback function that fires when the pan gesture ends on this element.
function onPanEnd(event, info) {
console.log(info.point.x, info.point.y)
}
<motion.div onPanEnd={onPanEnd} />
event: MouseEvent | TouchEvent | PointerEvent The originating pointer event. |
---|
info: PanInfo A
|
# Drag
The drag gesture follows the rules of the pan gesture but applies pointer movement to the x and/or y axis of the component.
<motion.div drag />
# Drag & Layout animations
If the draggable component is also used in layout animations, the drag gesture is applied to the component's viewport box instead of its x/y transforms.
<motion.div drag layout />
In this model, you can still animate and adjust x
and y
, but they will be additive to the gesture.
# drag: boolean | "x" | "y"
Enable dragging for this element. Set to false
by default. Set true
to drag in both directions. Set "x"
or "y"
to only drag in a specific direction.
<motion.div drag="x" />
# dragConstraints: false | Partial<BoundingBox2D> | RefObject<Element>
Applies constraints on the permitted draggable area.
It can accept an object of optional top
, left
, right
, and bottom
values, measured in pixels. This will define a distance the named edge of the draggable component.
Alternatively, it can accept a ref
to another component created with React's useRef
hook. This ref
should be passed both to the draggable component's dragConstraints
prop, and the ref
of the component you want to use as constraints.
// In pixels
<motion.div
drag="x"
dragConstraints={{ left: 0, right: 300 }}
/>
// As a ref to another component
const MyComponent = () => {
const constraintsRef = useRef(null)
return (
<motion.div ref={constraintsRef}>
<motion.div drag dragConstraints={constraintsRef} />
</motion.div>
)
}
# dragElastic: boolean | number
The degree of movement allowed outside constraints. 0 = no movement, 1 = full movement. Set to 0.5
by default.
<motion.div
drag
dragConstraints={{ left: 0, right: 300 }}
dragElastic={0.2}
/>
# dragMomentum: boolean
Apply momentum from the pan gesture to the component when dragging finishes. Set to true
by default.
<motion.div
drag
dragConstraints={{ left: 0, right: 300 }}
dragMomentum={false}
/>
# dragTransition: InertiaOptions
Allows you to change dragging inertia parameters. When releasing a draggable Frame, an animation with type inertia
starts. The animation is based on your dragging velocity. This property allows you to customize it. See Inertia for all properties you can use.
<motion.div
drag
dragTransition={{ bounceStiffness: 600, bounceDamping: 10 }}
/>
# dragPropagation: boolean
Allows drag gesture propagation to child components. Set to false
by default.
<motion.div drag="x" dragPropagation />
# dragControls: DragControls
Usually, dragging is initiated by pressing down on a component and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we might want to initiate dragging from a different component than the draggable one.
By creating a dragControls
using the useDragControls
hook, we can pass this into the draggable component's dragControls
prop. It exposes a start
method that can start dragging from pointer events on other components.
const dragControls = useDragControls()
function startDrag(event) {
dragControls.start(event, { snapToCursor: true })
}
return (
<>
<div onPointerDown={startDrag} />
<motion.div drag="x" dragControls={dragControls} />
</>
)
# onDrag(event, info): void
Callback function that fires when the component is dragged.
<motion.div
drag
onDrag={
(event, info) => console.log(info.point.x, info.point.y)
}
/>
event: MouseEvent | TouchEvent | PointerEvent |
---|
info: PanInfo |
# onDragStart(event, info): void
Callback function that fires when dragging starts.
<motion.div
drag
onDragStart={
(event, info) => console.log(info.point.x, info.point.y)
}
/>
event: MouseEvent | TouchEvent | PointerEvent |
---|
info: PanInfo |
# onDragEnd(event, info): void
Callback function that fires when dragging ends.
<motion.div
drag
onDragEnd={
(event, info) => console.log(info.point.x, info.point.y)
}
/>
event: MouseEvent | TouchEvent | PointerEvent |
---|
info: PanInfo |
# onDirectionLock(axis): void
Callback function that fires a drag direction is determined.
<motion.div
drag
dragDirectionLock
onDirectionLock={axis => console.log(axis)}
/>
axis: "x" | "y" |
---|