<!-- Source: https://motion.svelte.page/docs/use-animation-controls -->

# useAnimationControls

> Legacy imperative controls for coordinating one or more motion components.

**Source:** [https://motion.svelte.page/docs/use-animation-controls](https://motion.svelte.page/docs/use-animation-controls)

---

`useAnimationControls` creates a legacy imperative controller. Pass the returned
object to one or more `motion.*` components via `animate={controls}`, then call
`controls.start(...)`, `controls.set(...)`, or `controls.stop()`.

```svelte
<script lang="ts">
    import { motion, useAnimationControls } from '@humanspeak/svelte-motion'

    const controls = useAnimationControls()

    const run = async () => {
        await controls.start('launch')
        await controls.start('complete')
    }
</script>

<button onclick={run}>Start</button>
<motion.div
    animate={controls}
    variants={{
        launch: { x: 100 },
        complete: { x: 0, scale: 1.1 }
    }}
/>
```

> Live example: [/examples/use-animation-controls](https://motion.svelte.page/examples/use-animation-controls)

## Sequencing

`controls.start(definition)` returns a promise. If the controls are subscribed
to three motion components, that promise resolves after all three components
finish their animation.

```svelte
<script lang="ts">
    const controls = useAnimationControls()

    const submit = async () => {
        await controls.start('loading', { duration: 0.4 })
        await controls.start('success', { duration: 0.3 })
    }
</script>
```

## start vs set vs stop

The three commands have deliberately different relationships to time, matching upstream:

- **`start(definition)`** animates from the currently rendered value to the target — including the very first command on an element idling at a non-neutral pose (the animation departs from the idle value, never from a wiped base).
- **`set(definition)`** applies the values **instantly** — no animation, by design. It is the imperative "pose" primitive: `set('hidden')` then `start('visible')` is the canonical pose-then-animate pattern. If you expected motion and saw a snap, you wanted `start`.
- **`stop()`** freezes every in-flight animation **at its current mid-flight value** and holds it there — through later re-renders — rather than jumping to the target or reverting to a previous state.

## Wildcard and relative values

Keyframe wildcards and relative strings resolve against the element's **live value** the moment the animation starts:

```svelte
<script lang="ts">
    const controls = useAnimationControls()

    // [null, 100]: the live value feeds the FIRST keyframe, so this animates
    // from wherever x currently is → 100. (Later `null`s hold the previous
    // keyframe instead: [0, null] plays [0, 0].)
    const slideFromHere = () => controls.start({ x: [null, 100] })

    // Relative strings offset from the live value: x moves 50px further right.
    const nudge = () => controls.start({ x: '+=50' })

    // A bare `null` holds a channel at its current value — useful to pin one
    // channel while animating another.
    const pulseInPlace = () => controls.start({ scale: [null, 1.15, 1], x: null })
</script>
```

The same grammar works on the declarative `animate` prop. See the [keyframes example](/examples/keyframes) for the visual version.

## Variants

Each subscribed component resolves the same variant label against its own
`variants` map. This mirrors Motion's VisualElement fan-out behavior:

```svelte
<motion.div
    animate={controls}
    variants={{
        loading: { scale: 1.1 },
        success: { scale: 1 }
    }}
/>

<motion.span
    animate={controls}
    variants={{
        loading: { opacity: 0.5 },
        success: { opacity: 1 }
    }}
/>
```

## API Reference

### `useAnimationControls()`

Returns an `AnimationControls` object:

- **`start(definition, transitionOverride?)`** — animates every subscribed
  component and returns `Promise<unknown[]>`.
- **`set(definition)`** — synchronously sets every subscribed component to the
  target's final values. Variant labels and `transitionEnd` values are resolved.
- **`stop()`** — stops active subscriber animations.
- **`mount()`** — internal lifecycle hook called automatically by
  `useAnimationControls()`.
- **`subscribe(subscriber)`** — internal component subscription hook.

`start` and `set` throw if called before the hook's component has mounted,
matching upstream Motion's guard against render-synchronous control calls.

## Alias

`useAnimation` is exported as an alias of `useAnimationControls`, matching
Motion's legacy API.

## Related

- [`useAnimate`](/docs/use-animate) — scoped selector-based imperative animation
- [`Variants`](/docs/variants) — named animation states

Based on [Motion's useAnimationControls](https://motion.dev/docs/react-use-animation-controls) API.
