useLayout
Demo
Wraps Anime.js createLayout() into a Vue composable. Reactively re-creates the layout observer when the root element or params change, and reverts it automatically on unmount.
Usage
Animating a layout change
Use record() to snapshot current positions, mutate the DOM (via reactive state), then call animate() after the next tick so Anime.js interpolates from the old positions to the new ones.
<script setup lang="ts">
import { nextTick, ref, useTemplateRef } from "vue"
import { useLayout } from "@juleshry/vue-animejs"
const container = useTemplateRef("container")
const items = ref([1, 2, 3, 4])
const { record, animate } = useLayout(container)
async function shuffle() {
record()
items.value = [...items.value].sort(() => Math.random() - 0.5)
await nextTick()
animate({ duration: 600, ease: "outExpo" })
}
</script>
<template>
<div ref="container" class="grid">
<div v-for="item in items" :key="item">{{ item }}</div>
</div>
<button @click="shuffle">Shuffle</button>
</template>Using update() for non-reactive changes
When the layout change is purely DOM-driven (no Vue reactive state involved), update() records, runs the callback synchronously, then animates — all in one call.
WARNING
update() runs its callback synchronously. If your callback mutates Vue reactive state, the DOM won't have updated yet when animate() is called. In that case, use the record() + await nextTick() + animate() pattern instead.
<script setup lang="ts">
import { useTemplateRef } from "vue"
import { useLayout } from "@juleshry/vue-animejs"
const container = useTemplateRef("container")
const { layout, update } = useLayout(container)
function moveFirst() {
update(instance => {
const el = instance.root as HTMLElement
el.appendChild(el.firstElementChild!)
}, { duration: 500, ease: "outExpo" })
}
</script>
<template>
<div ref="container">
<div>A</div>
<div>B</div>
<div>C</div>
</div>
<button @click="moveFirst">Move first to end</button>
</template>Reactive params
Put a ref straight into the params object to change layout options reactively. The whole params object can also be a ref, a computed or a getter.
WARNING
Changing params destroys and re-creates the AutoLayout instance.
<script setup lang="ts">
import { ref, useTemplateRef } from "vue"
import { useLayout } from "@juleshry/vue-animejs"
const container = useTemplateRef("container")
const duration = ref(400)
const { record, animate } = useLayout(container, { ease: "outExpo", duration })
</script>Type Declarations
Show Type Declarations
export interface UseLayoutReturn {
/** The underlying Anime.js `AutoLayout` instance. `undefined` until the root element is available. */
layout: Readonly<ShallowRef<AutoLayout | undefined>>
/** Snapshots the current positions of all tracked children so the next layout change can be animated. */
record: () => void
/** Animates all children from their recorded positions to their new positions. */
animate: (params?: LayoutAnimationParams) => Timeline | undefined
/** Records, applies the callback (which triggers a layout change), then animates the transition. */
update: (callback: (layout: AutoLayout) => void, params?: LayoutAnimationParams) => void
/** Cancels the layout watcher and restores all elements to their original state. */
revert: () => void
}
/**
* Wraps Anime.js `createLayout()` into a Vue composable. Reactively re-creates the layout observer when the root element or params change, and reverts it automatically on unmount.
*
* @param root - The container element whose children will be tracked. Accepts a template ref, a CSS selector, or a reactive ref to either.
* @param params - Anime.js auto-layout parameters. Accepts a plain object, a ref / computed / getter, and refs as direct properties.
*/
export declare function useLayout(
root: MaybeRef<DOMTargetSelector> | MaybeComputedElementRef,
params?: MaybeRefOrGetter<MaybeRefOptions<AutoLayoutParams>>
): UseLayoutReturnReactivity Behavior
- The layout is created once the component has mounted, whatever form
roottakes (template ref, selector, element), so a selector can point into the component's own template, and server-rendered HTML is never touched before hydration. - A template ref that is still empty at mount (e.g. behind a
v-if) creates the layout as soon as it resolves. - If either
rootorparamschanges, the current layout is reverted and a new one is created. Forparams, that means a new object in a ref / computed / getter, a ref inside it changing, or a property of a reactive object mutated in place. - On component unmount, the layout watcher is stopped and the layout is reverted automatically.
TIP
When animating Vue-driven layout changes (v-for reorders, conditional rendering), always use record() → mutate state → await nextTick() → animate(). This ensures Anime.js captures positions before and after Vue has flushed its DOM update.
WARNING
layout is a readonly shallow ref. Do not mutate the Anime.js instance directly — use the returned methods instead.