Skip to content

useTimeline ​

Demo ​

Wraps Anime.js createTimeline() into a Vue composable. The timeline is created reactively — it re-creates whenever the options change — and is cancelled automatically on unmount.

Usage ​

Basic timeline ​

Chain animations on multiple elements sequentially using add.

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

const box1 = useTemplateRef<HTMLDivElement>("box1")
const box2 = useTemplateRef<HTMLDivElement>("box2")

const { add, play, restart } = useTimeline({ autoplay: false })

add(box1, { translateX: 200, duration: 600, ease: "outExpo" })
  .add(box2, { translateX: 200, duration: 600, ease: "outExpo" })
</script>

<template>
  <div ref="box1" class="box" />
  <div ref="box2" class="box" />
  <button @click="play">Play</button>
  <button @click="restart">Restart</button>
</template>

Overlapping animations with position ​

Pass a position string to add to offset when each animation starts relative to the previous one.

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

const box1 = useTemplateRef<HTMLDivElement>("box1")
const box2 = useTemplateRef<HTMLDivElement>("box2")
const box3 = useTemplateRef<HTMLDivElement>("box3")

const { add, play, restart } = useTimeline({ autoplay: false })

add(box1, { translateX: 200, duration: 600 })
  .add(box2, { translateX: 200, duration: 600 }, "-=300" /* starts 300 ms before box1 ends */)
  .add(box3, { translateX: 200, duration: 600 }, "+=100" /* starts 100 ms after box2 ends */)
</script>

<template>
  <div ref="box1" class="box" />
  <div ref="box2" class="box" />
  <div ref="box3" class="box" />
  <button @click="play">Play</button>
  <button @click="restart">Restart</button>
</template>

Setting initial values with set ​

Use set to pin a property to a value at a specific point in the timeline without animating it.

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

const box = useTemplateRef<HTMLDivElement>("box")

const { set, play } = useTimeline({ autoplay: false })

set(box, { opacity: 0 })
  .add(box, { opacity: 1, translateX: 200, duration: 800 })
</script>

<template>
  <div ref="box" class="box" />
  <button @click="play">Play</button>
</template>

Reactive options ​

Put a ref straight into the options object to control timeline-level parameters reactively. The whole options object can also be a ref, a computed or a getter. When options change the timeline is recreated and all previously recorded add/set/remove/call/label/sync calls are replayed automatically.

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

const box1 = useTemplateRef<HTMLDivElement>("box1")
const box2 = useTemplateRef<HTMLDivElement>("box2")
const loop = ref<boolean>(false)

const { add, play, restart } = useTimeline({ autoplay: false, loop })

add(box1, { translateX: 200, duration: 600, ease: "outExpo" })
  .add(box2, { translateX: 200, duration: 600, ease: "outExpo" }, "-=300")
</script>

<template>
  <div ref="box1" class="box" />
  <div ref="box2" class="box" />
  <label><input v-model="loop" type="checkbox" /> Loop</label>
  <button @click="play">Play</button>
  <button @click="restart">Restart</button>
</template>

Type Declarations ​

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

Show Type Declarations
ts
/**
 * What `add`, `set` and `remove` return: the live timeline, with its own `add` / `set` / `remove` swapped for the
 * composable's queueing ones. Before mount there's no timeline yet, so only `add` / `set` / `remove` are available.
 */
export type TimelineChain = Timeline & {
  /** Adds an animation to the timeline and returns a chainable object. */
  add: (
    targets: AnimationTargets,
    params: MaybeRefOrGetter<MaybeRefOptions<AnimationParams>>,
    position?: TimelinePosition | StaggerFunction<number | string>
  ) => TimelineChain
  /** Sets a property to a value at a point in the timeline without animating it, then returns a chainable object. */
  set: (
    targets: AnimationTargets,
    params: MaybeRefOrGetter<MaybeRefOptions<AnimationParams>>,
    position?: TimelinePosition
  ) => TimelineChain
  /** Removes an animation target (or a specific property) from the timeline and returns a chainable object. */
  remove: (targets: AnimationTargets, propertyName?: string) => TimelineChain
}

export interface UseTimelineReturn {
  /** The underlying Anime.js timeline instance. `undefined` until mounted. */
  timeline: Readonly<ShallowRef<Timeline | undefined>>
  /** Adds an animation to the timeline. Accepts a template ref or any valid Anime.js target. Returns a chainable object. */
  add: (
    targets: AnimationTargets,
    params: MaybeRefOrGetter<MaybeRefOptions<AnimationParams>>,
    position?: TimelinePosition | StaggerFunction<number | string>
  ) => TimelineChain
  /** Sets a property to a value at a point in the timeline without animating it. Returns a chainable object. */
  set: (
    targets: AnimationTargets,
    params: MaybeRefOrGetter<MaybeRefOptions<AnimationParams>>,
    position?: TimelinePosition
  ) => TimelineChain
  /** Removes an animation target (or a specific property) from the timeline. Returns a chainable object. */
  remove: (targets: AnimationTargets, propertyName?: string) => TimelineChain
  /** Synchronises another tickable (animation, timer) into the timeline at the given position. */
  sync: (synced?: Tickable, position?: TimelinePosition) => Timeline | undefined
  /** Adds a named label at a position so it can be referenced by `.add()` or `.seek()`. */
  label: (labelName: string, position?: TimelinePosition) => Timeline | undefined
  /** Inserts a callback function at a specific point in the timeline. */
  call: (callback: Callback<Timer>, position?: TimelinePosition) => Timeline | undefined
  /** Renders the timeline once without playing it. */
  init: (internalRender?: boolean) => Timeline | undefined
  /** Starts or resumes the timeline. */
  play: () => Timeline | undefined
  /** Reverses playback direction. */
  reverse: () => Timeline | undefined
  /** Pauses the timeline at the current position. */
  pause: () => Timeline | undefined
  /** Restarts the timeline from the beginning. */
  restart: () => Timeline | undefined
  /** Toggles between forward and reverse direction. */
  alternate: () => Timeline | undefined
  /** Resumes from a paused state. */
  resume: () => Timeline | undefined
  /** Jumps immediately to the end of the timeline. */
  complete: () => Timeline | undefined
  /** Resets the timeline to its initial state. Pass `true` for a soft reset that preserves the current cycle. */
  reset: (softReset?: boolean) => Timeline | undefined
  /** Stops the timeline and removes it from the Anime.js engine. */
  cancel: () => Timeline | undefined
  /** Cancels the timeline and restores all animated properties to their original values. */
  revert: () => Timeline | undefined
  /** Seeks to a specific time (in ms). */
  seek: (time: number, muteCallbacks?: boolean | number, internalRender?: boolean | number) => Timeline | undefined
  /** Rescales the timeline to a new total duration. */
  stretch: (newDuration: number) => Timeline | undefined
  /** Re-reads the current values of all animated properties from the DOM. */
  refresh: () => Timeline | undefined
}

/**
 * Wraps Anime.js `createTimeline()` into a Vue composable. Reactively re-creates the timeline when options change, and cancels it automatically on unmount.
 *
 * `add`, `set`, `remove`, `call`, `label` and `sync` calls are recorded in order: made before mount, they apply once
 * the timeline exists, and when options change they're all replayed on the new timeline.
 *
 * @param options - Anime.js timeline parameters. Accepts a plain object, a ref / computed / getter, and refs as direct properties. Defaults to `{}`.
 */
export declare function useTimeline(options?: MaybeRefOrGetter<MaybeRefOptions<TimelineParams>>): UseTimelineReturn

Reactivity & Lifecycle Behavior ​

  • The timeline is created on mount, not at setup() time — timeline is undefined until then (this keeps useTimeline safe to call during SSR, where onMounted never runs). Control methods other than add/set/call/remove are no-ops and return undefined while timeline is unset.
  • add, set, remove, call, label and sync calls are recorded in order. Made before mount, they're applied once the component mounts — safe to call at the top level of <script setup>.
  • If options changes after mount (a new object in a ref / computed / getter, a ref inside it changing, or a property of a reactive object mutated in place), the current timeline is reverted and recreated. Every recorded call is replayed on the new timeline in the original order, so a target you removed stays removed and labels still resolve.
  • The params of add and set accept the same shapes as options, but they're read when the timeline is (re)built, not watched: changing a ref inside them takes effect the next time options changes.
  • On component unmount, the watcher and timeline are cleaned up automatically.

TIP

add, set and remove return a TimelineChain: the live timeline, so you can chain .add() / .set() calls fluently and still reach every timeline method and property (play(), seek(), duration, ...). Before mount, only .add(), .set() and .remove() exist on it.

ts
const { add } = useTimeline({ autoplay: false })
onMounted(() => {
  add(box1, { translateX: 100, duration: 400 })
    .add(box2, { translateY: 100, duration: 400 })
    .add(box3, { rotate: 360, duration: 400 })
    .play()
})

WARNING

timeline is a readonly shallow ref. Do not mutate the Anime.js instance directly — use the returned control methods instead.

Source ​

Github