Skip to content

v-draggable ​

Demo ​

Drag the box around

Applies an Anime.js createDraggable() directly from a template attribute. Makes the element draggable on mount, re-creates it when the binding value changes, and reverts it on unmount.

Usage ​

Basic draggable ​

vue
<script setup lang="ts">
import { vDraggable } from "@juleshry/vue-animejs"
</script>

<template>
  <div v-draggable class="box" />
</template>

Constrained to a container ​

Pass a container element via the container option to keep the element within bounds.

WARNING

container must be resolved inside a computed — a template ref's .value is null at setup time, so passing it directly in a plain object will result in an unconstrained draggable.

vue
<script setup lang="ts">
import { computed, useTemplateRef } from "vue"
import { vDraggable } from "@juleshry/vue-animejs"

const container = useTemplateRef("container")

const options = computed(() => ({
  container: container.value,
  containerPadding: 10,
}))
</script>

<template>
  <div ref="container" class="stage">
    <div v-draggable="options" class="box" />
  </div>
</template>

Reactive options ​

Bind a ref or computed — the draggable is reverted and re-created whenever the value changes.

WARNING

Changing options reverts the current draggable instance and creates a new one from scratch, resetting its position within the container.

vue
<script setup lang="ts">
import { ref, computed } from "vue"
import { vDraggable } from "@juleshry/vue-animejs"

const locked = ref(false)

const options = computed(() => ({
  x: !locked.value,
  y: !locked.value,
}))
</script>

<template>
  <div v-draggable="options" class="box" />
  <button @click="locked = !locked">Toggle lock</button>
</template>

Type Declarations ​

For all available options, see the Anime.js draggable documentation.

Show Type Declarations
ts
/**
 * Declarative draggable directive. Makes the element draggable on mount (once the mount flush has
 * finished, so components' own `onMounted` hooks still see the rendered DOM), re-creates it when the
 * binding value changes, and reverts it on unmount.
 *
 * @example
 * <div v-draggable />
 * <div v-draggable="{ snap: [0, 100] }" />
 */
export declare const vDraggable: Directive<HTMLElement, DraggableParams | undefined>

Lifecycle Behavior ​

  • On mount, the draggable is created from the binding value. It runs once the mount flush has finished, so the onMounted hooks of the surrounding components (including ones whose root is this element) still see the DOM as rendered. That's still before the browser paints.
  • On update, if the binding value changed, the previous draggable is reverted, then a new one is created from the new value.
  • The binding value is compared by content, not by reference: an inline object (v-draggable="{ … }") that re-renders with the same values doesn't restart anything. Functions count as unchanged when their source is the same, so an inline stagger(60) doesn't restart on every render either, but changing only its argument isn't picked up. Add a :key to the element to force a fresh start in that case.
  • On unmount, the draggable is reverted, restoring the element's original styles.

TIP

The directive works with no binding value at all — v-draggable alone makes the element freely draggable with default options.

WARNING

Anime.js instances are tracked per-element internally. Don't apply v-draggable to a component root that gets recycled by v-for with a non-stable key — use a stable key so Vue doesn't swap the underlying element unexpectedly.

Source ​

Github