Skip to content

v-svg-drawable ​

Demo ​

Wraps an SVG geometry element in Anime.js's drawable Proxy and animates its draw property directly from a template attribute. The animation is created on mount, re-created whenever the binding value changes, and reverted automatically on unmount.

Usage ​

Basic draw animation ​

Values are normalised: 0 = hidden, 1 = fully drawn.

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

<template>
  <svg viewBox="0 0 200 100">
    <path
      v-svg-drawable="{ draw: '0 1', duration: 1500, ease: 'inOutQuad' }"
      d="M10 50 Q100 10 190 50"
      fill="none"
      stroke="currentColor"
      stroke-width="3"
    />
  </svg>
</template>

Keyframe draw animation ​

Use an array of draw keyframes for more expressive stroke animations. Each keyframe is a "start end" string with normalised values.

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

// Hidden → second half only → fully drawn
const options = {
  draw: ["0 0", "0.5 1", "0 1"],
  duration: 1200,
  ease: "inOutQuad",
}
</script>

<template>
  <svg viewBox="0 0 100 100">
    <circle v-svg-drawable="options" cx="50" cy="50" r="40" fill="none" stroke="currentColor" stroke-width="4" />
  </svg>
</template>

Reactive options ​

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

WARNING

Changing options recreates the drawable and its animation from scratch.

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

const duration = ref(1200)

const options = computed(() => ({
  draw: "0 1",
  duration: duration.value,
  ease: "outExpo",
}))
</script>

<template>
  <svg viewBox="0 0 200 100">
    <path
      v-svg-drawable="options"
      d="M10 50 Q100 10 190 50"
      fill="none"
      stroke="currentColor"
      stroke-width="3"
    />
  </svg>
  <input type="range" min="200" max="3000" v-model.number="duration" />
</template>

Type Declarations ​

Show Type Declarations
ts
/**
 * Declarative SVG drawable directive. Wraps the SVG geometry element in an Anime.js drawable
 * Proxy and animates the `draw` property 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 on unmount.
 *
 * During SSR, renders the path's starting draw state (see `getInitialDraw`) so it doesn't show fully
 * drawn before hydration.
 *
 * @example
 * <path v-svg-drawable="{ draw: '0 1', duration: 1200 }" />
 * <path v-svg-drawable="{ draw: ['0 0', '0.5 1', '0 1'], ease: 'inOutQuad', loop: true }" />
 */
export declare const vSvgDrawable: Directive<SVGGeometryElement, AnimationParams | undefined>

Lifecycle Behavior ​

  • On mount, the element is wrapped in a drawable Proxy and, if a binding value is provided, the draw animation is created. 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 animation is cancelled and reverted, the pathLength, stroke-dasharray, stroke-dashoffset and stroke-linecap the drawable wrote are reset to your own values (or removed), then a new drawable and animation are created from the new value, starting from the same state as on mount.
  • The binding value is compared by content, not by reference: an inline object (v-svg-drawable="{ … }") 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 animation is cancelled and reverted.
  • During SSR, the path is rendered in its starting draw state (pathLength, stroke-dasharray, stroke-dashoffset), so it doesn't show fully drawn before hydration. That's the from of draw: [from, to] / draw: { from }, or nothing drawn ("0 0") otherwise, matching what svg.createDrawable() paints first. The attributes are removed again before the drawable is created on the client. See SSR.

TIP

draw values are normalised: 0 = start of path, 1 = end of path. The format is "startPosition endPosition" — e.g. "0 1" draws the full stroke, "0.25 0.75" draws only the middle half.

WARNING

Only use v-svg-drawable on SVG geometry elements (path, circle, line, polyline, polygon, rect, ellipse) — it relies on Anime.js's svg.createDrawable(), which requires a measurable stroke path.

Source ​

Github