useAnimate
useAnimate wraps the animate
function from AnimeJS. It animates DOM elements, SVG nodes, and JavaScript
objects.
When reactive parameters change, nanime rebuilds the underlying animation.
By default it restarts. Set keepTime: true to carry the playhead over so
playback continues instead.
Usage
{"x":0,"y":0}
import { round } from '#nanime/utils'
const vector2D = shallowReactive({ x: 0, y: 0 })
useAnimate(vector2D, {
x: 100,
y: 150,
duration: 10000,
modifier: round(0),
ease: 'outElastic',
alternate: true,
loop: true,
})
<template>
<p>
{{ JSON.stringify(vector2D) }}
</p>
</template>
Signature
function useAnimate(
target: AnimeTargets,
parameters?: MaybeRefOrGetter<AnimationParams>,
options?: NanimeInstanceOptions
): JSAnimation
Parameters
ref, a reactive() object or a getter. The
animation rebuilds when it changes. See the
AnimeJS animation documentation
for the options.| Option | Type | Default |
|---|---|---|
keepTime | boolean | false |
Returns
Returns a reactive proxy around the AnimeJS JSAnimation.
Its methods and properties are in the
AnimeJS animation documentation.
Call methods on the returned object, as in animation.play(), because a
destructured method throws.
Properties like progress don't update a template
either.
Caveats
Plain object targets must be reactive or shallowReactive
// wrong: AnimeJS writes to the object's properties directly, bypassing .value
const position = ref({ x: 0, y: 0 })
useAnimate(position, { x: 100 })
// right: AnimeJS animates reactive object properties directly
const position = reactive({ x: 0, y: 0 })
useAnimate(position, { x: 100 })
Passing a ref holding an object fails because AnimeJS writes
directly to properties on the target without touching .value. The mutations
bypass Vue's reactivity system and the template never updates. Wrap plain
objects in reactive or shallowReactive
instead so property updates trigger re-renders.
Stagger a list that grows through a getter
// wrong: the stagger works out its steps for the first list only
useAnimate(items, { y: -16, delay: stagger(80), loop: true })
// right: a getter makes a new stagger for every rebuild
useAnimate(items, () => ({ y: -16, delay: stagger(80), loop: true }), { keepTime: true })
AnimeJS stagger() works out each item's step the first time it runs and
keeps them. Adding an item rebuilds the animation with the same parameters, so
the new item gets the first item's step. A getter creates a fresh stagger on
each rebuild, and keepTime: true keeps the others from restarting.
Do not destructure the returned instance
// wrong: play() throws when called
const { play } = useAnimate('.target', { opacity: 0 })
// right
const animation = useAnimate('.target', { opacity: 0 })
animation.play()
The proxy hands back methods without their instance, so a destructured
play() runs with no this and throws. A destructured property such as
progress is a copy of the value at that moment. Keep the returned object
and call methods on it.
Properties do not update templates
// wrong: progress never updates inside a template interpolation
// <p>{{ animation.progress }}</p>
// right: update a ref inside onUpdate
const progress = ref(0)
const animation = useAnimate('.target', {
opacity: [0, 1],
onUpdate: self => progress.value = self.progress,
})
Interpolating properties like progress in a template displays only their
initial value. AnimeJS updates them outside Vue's reactivity tracking, so
nothing triggers a re-render. Assign the current value to a
ref inside the onUpdate callback to drive reactive
template elements.
Type reference
type AnimeTargets = TargetsParam | MaybeElementRef | MaybeElementRef[]
interface NanimeInstanceOptions {
keepTime?: boolean
}
See also
useWaapiAnimate: compositor-thread animation engineuseAnimatable: accessor-driven animation for fast events- Road trip: a car driven by scroll, a split-text title and a multi-step booking log
- AnimeJS animate documentation: upstream parameters and property options