Composables
useDraggable
Documentation for useDraggable composable.
The useDraggable composable wraps the createDraggable function from AnimeJS.
Arguments
target
DraggableTypes['target'] required
The element(s) to make draggable. Supports CSS selectors, DOM elements, or template refs.
options
DraggableOptions
Configuration for dragging behavior. Highly reactive, supporting Vue refs and getters for most properties.
Example
0%
35%
75%
import { spring } from '#nanime/easings'
const container = useTemplateRef('container')
const draggable = useTemplateRef('draggable')
useDraggable(draggable, {
container: container,
releaseEase: spring({
bounce: 0.65,
duration: 400,
}),
x: {
snap: (draggable) => {
const hw = (draggable?.dragArea || [0, 0, 0, 0])[2]
const results = [0, hw * 0.35, hw * 0.75]
return results
},
},
})
<template>
<div
ref="container"
class="relative rounded-md border-dashed h-10"
>
<div class="snap-positions">
<div
class="spot"
style="--snap-x: 0%"
>
0%
</div>
<div
class="spot"
style="--snap-x: 35%"
>
35%
</div>
<div
class="spot"
style="--snap-x: 75%"
>
75%
</div>
</div>
<div
ref="draggable"
class="simple-box w-12 h-10! draggable"
/>
</div>
</template>
@reference "~/assets/css/main.css";
.snap-positions {
@apply absolute inset-0 pointer-events-none;
}
.snap-positions div {
@apply absolute top-0 inset-y-0 left-(--snap-x) w-12 h-10;
}
.draggable {
@apply z-10 text-black relative;
}
Return Value
Returns a BufferedProxyReturns<Draggable> object, a reactive proxy around the AnimeJS Draggable instance. Methods stay callable before the instance exists: calls made early are buffered and replayed once it is created.
API
Types
function useDraggable(
target: DraggableTypes["target"],
options?: DraggableOptions
): BufferedProxyReturns<Draggable>
type DraggableTargets = DOMTargetSelector | MaybeElementRef<HTMLElement | SVGElement | VueInstance | null> | null | undefined;
type DraggableTargetContainer = DraggableParams["container"] | MaybeElementRef<HTMLElement | VueInstance | null> | null | undefined;
type DraggableTargetTrigger = DraggableParams["trigger"] | DraggableTargets;
type DraggableTypes = {
target: DraggableTargets;
trigger: DraggableTargetTrigger;
container: DraggableTargetContainer;
}
type DraggableOptions = MakeRefable<Omit<DraggableParams, 'trigger' | 'container' | 'x' | 'y'> & {
trigger?: DraggableTypes["trigger"]
container?: DraggableTypes["container"]
x?: boolean | Prettify<MakeRefable<DraggableAxisParam, "snap", Draggable>>
y?: boolean | Prettify<MakeRefable<DraggableAxisParam, "snap", Draggable>>
}, RefableProps, Draggable>
See AnimeJS Draggable documentation for more details.
Caveats
- Property reads can be
undefineduntil the instance exists. The draggable is created after mount, and only once the target resolves. Methods are safe to call before that, butx,yand other properties have nothing to report yet. - Never put a CSS transition on
transform.useDraggablewrites an inlinetransformevery frame, and a transition on that property fights those writes and makes dragging feel laggy. Transition another property, or move the visual change onto a child element. xandyreport the element's current position, not how far this drag has moved it. They read whatever property the axis maps to,translateXandtranslateYby default or themapToproperty if you set one, so they carry over from previous drags instead of starting at0.- Resizing the target schedules a refresh. AnimeJS attaches a
ResizeObserverto the element, and any size change trips it, including one made with the standalone CSSscaleproperty. Animate a child element when the target itself does not need to resize.
const draggable = useDraggable('.square')
// callable immediately, buffered until the instance exists
const disable = () => draggable.disable()
For a true drag delta, record x and y in onGrab and subtract:
let origin = { x: 0, y: 0 }
useDraggable(target, {
onGrab: self => origin = { x: self.x, y: self.y },
onUpdate: self => moved(self.x - origin.x, self.y - origin.y),
})