Drag
drag makes an element follow the pointer. drag="x" or drag="y" keeps it to one axis. On release it carries on at the speed it was thrown, and slows to a stop.
import { drag, LazyWardic, w } from "@wardic/solid/unstable/v2"
/** A ball held inside its arena, and one that drags across and springs back. */
export default function Drag() {
let arena!: HTMLDivElement
return (
// This page drags, so it brings `drag` itself: pages that don't never load it.
<LazyWardic features={[drag]}>
<div class="sample-arena" ref={arena}>
<w.div class="sample-ball" drag dragConstraints={() => arena} dragElastic={0.2} whileDrag={{ scale: 1.15 }} />
<w.div class="sample-ball sample-ball-alt" drag="x" dragSnapToOrigin whileDrag={{ scale: 1.15 }} />
</div>
</LazyWardic>
)
}import { drag, LazyWardic, w } from "@wardic/solid"
/** A ball held inside its arena, and one that drags across and springs back. */
export default function Drag() {
let arena!: HTMLDivElement
return (
// This page drags, so it brings `drag` itself: pages that don't never load it.
<LazyWardic features={[drag]}>
<div class="sample-arena" ref={arena}>
<w.div class="sample-ball" drag dragConstraints={() => arena} dragElastic={0.2} whileDrag={{ scale: 1.15 }} />
<w.div class="sample-ball sample-ball-alt" drag="x" dragSnapToOrigin whileDrag={{ scale: 1.15 }} />
</div>
</LazyWardic>
)
}import { motion } from "motion/react"
import { useRef } from "react"
/** A ball held inside its arena, and one that drags across and springs back. */
export default function Drag() {
const arena = useRef<HTMLDivElement>(null)
return (
<div className="sample-arena" ref={arena}>
<motion.div className="sample-ball" drag dragConstraints={arena} dragElastic={0.2} whileDrag={{ scale: 1.15 }} />
<motion.div className="sample-ball sample-ball-alt" drag="x" dragSnapToOrigin whileDrag={{ scale: 1.15 }} />
</div>
)
}The Feature
Dragging is the drag Feature. A page that drags brings it itself, through <LazyWardic features={[drag]}>, as the demo above does, so the pages that don't never load its code.
Constraints
dragConstraints keeps the element inside a box, { left, right, top, bottom } in pixels from where it started, or inside another element, given as an accessor (() => arena). dragElastic, 0 to 1, is how far it can be pulled past them; it springs back on release.
Release
dragMomentum={false}stops it where it's released.dragSnapToOriginsprings it back to where it started.dragTransitiontunes the glide (power,timeConstant,bounceStiffness,bounceDamping).
While dragging
whileDrag applies while it's dragged, as whileTap does while it's pressed.
One axis, decided by the gesture
dragDirectionLock waits for the pointer to move 10 px along an axis, then drags on that one alone. The drag starts at that moment, so a card that drags sideways inside a sheet that drags down doesn't start on a vertical swipe.
Drags inside drags
A drag claims its axes as it starts, so a draggable inside a draggable drags on its own, and its ancestor stays put for that gesture. dragPropagation claims nothing, so the ancestor drags with it.
Starting from somewhere else
createDragControls() starts a drag from another element, such as a handle:
const controls = createDragControls()
<div class="handle" onPointerDown={(event) => controls.start(event)} />
<w.div drag="y" dragControls={controls} dragListener={false} />Inside a zoomed parent
An element inside a scaled or rotated parent moves in the parent's pixels, so it runs ahead of the pointer or lags behind it. <WardicConfig transformPagePoint={correctParentTransform(() => plan)}> maps the pointer through the parent's transform, so it stays under the pointer at any zoom. Arrange a room shows it.