# Anime.js User Guide

## 1. Quick Start

Anime.js is a lightweight, high-performance JavaScript animation engine that handles CSS properties, SVG attributes, DOM attributes, and JavaScript objects with a simple, unified API. Version 4 introduces timelines, draggables, scroll-linked animations, FLIP-style layout animations, text splitting, and Web Animations API (WAAPI) integration.

**Key features:**

- Animate CSS transforms, colors, SVG attributes, and plain objects
- Spring physics, cubic-bezier, steps, and 20+ built-in easing functions
- Timelines with labels, position offsets, and sync
- Scroll-linked animations via `ScrollObserver`
- Drag interactions with snap, inertia, and container bounds
- FLIP-style automatic layout animations (`AutoLayout`)
- Text splitting into lines, words, and characters
- Native WAAPI bridge for GPU-accelerated animations
- Stagger utility with grid support
- Tree-shakeable ES module architecture

**Prerequisites:** A modern browser (Chrome 84+, Firefox 75+, Safari 14+, Edge 84+) or Node.js 18+.

**Install and animate in under a minute:**

```bash
npm install animejs
```

```javascript
import { animate } from 'animejs';

animate('.box', {
  translateX: 250,
  rotate: '1turn',
  duration: 800,
  ease: 'out(3)',
});
```

```text
// The .box element slides 250px right and rotates one full turn over 800ms
```

> [!TIP]
> For quick prototyping, load the UMD bundle directly from a CDN: `<script src="https://cdn.jsdelivr.net/npm/animejs@4/dist/bundles/anime.umd.min.js"></script>`. The global `anime` object exposes all exports.

**What to try next:** Jump to Installation for all delivery methods, or skip straight to Core Usage to explore animations, timelines, and scroll-linked effects.

## 2. Installation

### System Requirements

| Requirement | Minimum |
|---|---|
| Node.js (for npm) | 18+ |
| Browser | Chrome 84+, Firefox 75+, Safari 14+, Edge 84+ |
| Module system | ES Modules or CommonJS |

### Installation Methods

=== "npm"
    ```bash
    npm install animejs
    ```

=== "yarn"
    ```bash
    yarn add animejs
    ```

=== "pnpm"
    ```bash
    pnpm add animejs
    ```

=== "CDN (UMD)"
    ```html
    <script src="https://cdn.jsdelivr.net/npm/animejs@4/dist/bundles/anime.umd.min.js"></script>
    ```

=== "CDN (ES Module)"
    ```html
    <script type="module">
      import { animate } from 'https://cdn.jsdelivr.net/npm/animejs@4/dist/modules/index.js';
    </script>
    ```

### Module Imports

Anime.js v4 supports tree-shaking. Import only what you need:

```javascript
// Full library
import { animate, createTimeline, createTimer, engine } from 'animejs';

// Individual modules for smaller bundles
import { animate } from 'animejs/animation';
import { createTimeline } from 'animejs/timeline';
import { createTimer } from 'animejs/timer';
import { createAnimatable } from 'animejs/animatable';
import { Draggable } from 'animejs/draggable';
import { createScope } from 'animejs/scope';
import { engine } from 'animejs/engine';
import { ScrollObserver } from 'animejs/events';
import { AutoLayout } from 'animejs/layout';
import * as easings from 'animejs/easings';
import * as utils from 'animejs/utils';
import * as svg from 'animejs/svg';
import * as text from 'animejs/text';
import { waapi } from 'animejs/waapi';
```

> [!IMPORTANT]
> The package uses `"type": "module"` in `package.json`. CommonJS consumers should use `require('animejs')` which resolves to `dist/modules/index.cjs`.

### Post-Install Verification

```javascript
import { engine } from 'animejs';
console.log(engine.timeUnit); // 'ms'
```

```text
ms
```

### Install Troubleshooting

**Q: I get "Cannot find module 'animejs'" after install.**

Verify the package is in `node_modules/animejs` and your bundler supports the `exports` field in `package.json`. Older bundlers may need the `module` field: `animejs/dist/modules/index.js`.

**Q: TypeScript types are missing.**

Types ship at `dist/modules/index.d.ts`. Ensure your `tsconfig.json` has `"moduleResolution": "bundler"` or `"node16"`.

**Q: The UMD bundle doesn't expose `anime` globally.**

Make sure you're loading `anime.umd.min.js` (not the ES module build) via a regular `<script>` tag (not `type="module"`).

## 3. Configuration

### Global Defaults

Override global defaults through the `engine.defaults` object. These apply to every new animation unless overridden per-instance:

```javascript
import { engine } from 'animejs';

// Change the default easing and duration
engine.defaults.ease = 'inOut(3)';
engine.defaults.duration = 600;
engine.defaults.autoplay = true;
```

| Default | Type | Value | Description |
|---|---|---|---|
| `duration` | Number | `1000` | Animation duration in ms |
| `delay` | Number | `0` | Delay before start in ms |
| `ease` | String/Function | `'out(2)'` | Default easing |
| `loop` | Number/Boolean | `0` | Number of extra iterations (`true` = infinite) |
| `alternate` | Boolean | `false` | Reverse direction on each loop |
| `reversed` | Boolean | `false` | Start playing in reverse |
| `autoplay` | Boolean | `true` | Start immediately on creation |
| `playbackRate` | Number | `1` | Playback speed multiplier |
| `frameRate` | Number | `240` | Max frames per second |
| `composition` | String/Number | `'replace'` | Tween composition mode (`'replace'`, `'none'`, `'blend'`) |
| `loopDelay` | Number | `0` | Delay between loop iterations |

### Engine Settings

```javascript
import { engine } from 'animejs';

// Switch to seconds-based timing
engine.timeUnit = 's';

// Control rounding precision (decimal places)
engine.precision = 4;

// Adjust global playback speed
engine.speed = 1.5;

// Disable auto-pause when the browser tab is hidden
engine.pauseOnDocumentHidden = false;
```

> [!TIP]
> When using `engine.timeUnit = 's'`, all duration and delay values are interpreted in seconds. For example, `duration: 1` means 1 second. The default is `'ms'` (milliseconds).

See the Admin Guide for the full configuration reference including all engine properties and composition internals.

## 4. Core Usage

### Creating Animations

The `animate()` function is the primary way to create animations:

```javascript
import { animate } from 'animejs';

// Animate CSS properties
animate('.element', {
  translateX: 250,
  opacity: [0, 1],
  duration: 1000,
  ease: 'out(3)',
});
```

**Targets** can be:
- CSS selectors: `'.class'`, `'#id'`, `'div'`
- DOM elements: `document.querySelector('.box')`
- NodeLists: `document.querySelectorAll('.items')`
- Arrays: `[el1, el2, el3]`
- JavaScript objects: `{ x: 0, y: 0 }`

**Animatable properties** include:
- CSS transforms: `translateX`, `translateY`, `translateZ`, `rotate`, `rotateX`, `rotateY`, `rotateZ`, `scale`, `scaleX`, `scaleY`, `scaleZ`, `skew`, `skewX`, `skewY`
- Short transforms: `x` (translateX), `y` (translateY), `z` (translateZ)
- CSS properties: `opacity`, `width`, `height`, `color`, `backgroundColor`, etc.
- CSS variables: `'--custom-property'`
- SVG attributes: `cx`, `r`, `d`, `points`, `fill`, `stroke`, etc.
- DOM attributes: any attribute via `setAttribute`
- Object properties: any numeric property on a JS object

### Value Types

```javascript
// Unitless numbers
animate('.box', { translateX: 250 }); // defaults to 'px' for transforms

// With units
animate('.box', { width: '80%', rotate: '1turn' });

// From-to values (array syntax)
animate('.box', { translateX: [0, 250] });

// Multiple keyframes (array of values)
animate('.box', { translateX: [0, 100, 50, 250] });

// Relative values (operators)
animate('.box', { translateX: '+=200' }); // add 200 to current
animate('.box', { translateX: '-=100' }); // subtract 100
animate('.box', { translateX: '*=2' });   // multiply by 2

// Colors (hex, rgb, rgba, hsl, hsla)
animate('.box', { backgroundColor: '#FF0000' });
animate('.box', { color: 'rgb(255, 0, 0)' });

// CSS variables
animate('.box', { '--my-var': [0, 100] });

// Function-based values (per-target)
animate('.items', {
  translateX: (target, index, total) => index * 50,
  duration: (target, index, total) => 500 + index * 100,
});
```

> [!NOTE]
> Color transitions always convert to RGBA internally. Named CSS colors (like `red`, `blue`) are not supported — use hex, rgb(), rgba(), hsl(), or hsla() notation.

### Keyframes

Anime.js supports two keyframe formats:

```javascript
// Duration-based keyframes (array of objects)
animate('.box', {
  keyframes: [
    { translateX: 100, rotate: '45deg' },
    { translateX: 250, rotate: '90deg' },
    { translateX: 0, rotate: '0deg' },
  ],
  duration: 2000,
});

// Percentage-based keyframes (object with percent keys)
animate('.box', {
  keyframes: {
    '0%':   { translateX: 0 },
    '50%':  { translateX: 250, ease: 'out(3)' },
    '100%': { translateX: 100 },
  },
  duration: 2000,
});
```

### Easing Functions

Anime.js includes a comprehensive set of easing functions:

| Easing | Syntax | Description |
|---|---|---|
| Power | `'out(2)'`, `'in(3)'`, `'inOut(4)'` | Customizable power curves |
| Named | `'inQuad'`, `'outCubic'`, `'inOutQuart'` | Standard Robert Penner easings |
| Back | `'inBack(1.7)'`, `'outBack'` | Overshoot easing |
| Elastic | `'inElastic(1, 0.3)'`, `'outElastic'` | Springy bounce |
| Bounce | `'inBounce'`, `'outBounce'`, `'inOutBounce'` | Ball-drop bounce |
| Sine | `'inSine'`, `'outSine'`, `'inOutSine'` | Sinusoidal |
| Circ | `'inCirc'`, `'outCirc'`, `'inOutCirc'` | Circular |
| Expo | `'inExpo'`, `'outExpo'`, `'inOutExpo'` | Exponential |
| Linear | `'linear'` | Constant speed |
| Steps | `steps(5)` | Step function |
| Cubic Bezier | `cubicBezier(0.42, 0, 0.58, 1)` | CSS-style bezier |
| Spring | `spring({ mass: 1, stiffness: 100, damping: 10 })` | Physics-based spring |

```javascript
import { animate, spring } from 'animejs';

// Spring easing with physics parameters
animate('.box', {
  translateX: 250,
  ease: spring({ mass: 1, stiffness: 100, damping: 10, velocity: 0 }),
});

// Springs also accept bounce + duration shorthand
animate('.box', {
  translateX: 250,
  ease: spring({ bounce: 0.5, duration: 628 }),
});
```

> [!WARNING]
> The `spring()` easing automatically calculates its own duration based on the physics simulation. Any `duration` parameter you set on the animation will be overridden by the spring's settling duration.

### Easing Directions

Every easing supports four direction variants:

| Direction | Effect |
|---|---|
| `in` | Starts slow, ends fast |
| `out` | Starts fast, ends slow (default for power) |
| `inOut` | Slow at both ends |
| `outIn` | Fast at both ends |

```javascript
animate('.box', { translateX: 250, ease: 'inOutQuart' });
animate('.box', { translateX: 250, ease: 'outIn(3)' });
```

### Playback Controls

Every animation, timer, and timeline returns an instance with full playback control:

```javascript
const anim = animate('.box', {
  translateX: 250,
  autoplay: false,
});

anim.play();       // Play forward
anim.pause();      // Pause
anim.resume();     // Resume from current position
anim.reverse();    // Play backward
anim.alternate();  // Toggle direction
anim.restart();    // Reset and play
anim.seek(500);    // Jump to 500ms
anim.reset();      // Reset to initial state
anim.cancel();     // Cancel and detach
anim.revert();     // Cancel and restore original CSS
anim.complete();   // Jump to end and cancel

// Properties (get and set)
anim.currentTime = 500;
anim.progress = 0.5;         // 0 to 1
anim.iterationProgress = 0.5;
anim.currentIteration = 2;
anim.speed = 2;              // 2x playback speed
anim.reversed = true;
```

### Callbacks

```javascript
animate('.box', {
  translateX: 250,
  duration: 1000,
  loop: 2,
  onBegin: (anim) => console.log('Animation started'),
  onBeforeUpdate: (anim) => {},
  onUpdate: (anim) => console.log(`Progress: ${anim.progress}`),
  onRender: (anim) => console.log('Frame rendered'),
  onLoop: (anim) => console.log(`Loop ${anim.currentIteration}`),
  onPause: (anim) => console.log('Paused'),
  onComplete: (anim) => console.log('Done!'),
});
```

### Promises

Animations are thenable — use `await` or `.then()`:

```javascript
const anim = animate('.box', { translateX: 250 });

// Await completion
await anim;
console.log('Animation finished');

// Or use .then()
anim.then(() => console.log('Animation finished'));
```

### Timers

`createTimer()` creates a time-based ticker without animation targets. Useful for countdowns, progress bars, or orchestrating custom logic:

```javascript
import { createTimer } from 'animejs';

const timer = createTimer({
  duration: 5000,
  loop: true,
  onUpdate: (self) => {
    console.log(`Time: ${self.currentTime}ms, Progress: ${self.progress}`);
  },
});
```

## 5. Common Tasks

### Timelines

Chain and orchestrate multiple animations on a shared timeline:

```javascript
import { createTimeline } from 'animejs';

const tl = createTimeline({
  defaults: { duration: 600, ease: 'out(3)' },
});

tl.add('.box-1', { translateX: 250 })
  .add('.box-2', { translateY: 100 }, '<')       // Start with previous
  .add('.box-3', { rotate: '1turn' }, '<<')      // Start at previous start
  .add('.box-4', { scale: [0, 1] }, '-=200')     // 200ms before end
  .add('.box-5', { opacity: [0, 1] }, '+=100');  // 100ms after end
```

**Position syntax:**

| Position | Meaning |
|---|---|
| *(omitted)* | After previous child ends |
| `'<'` | At the end of the previous child |
| `'<<'` | At the start of the previous child |
| `'+=200'` | 200ms after the current end |
| `'-=200'` | 200ms before the current end |
| `500` | Absolute position at 500ms |
| `'labelName'` | At a named label |
| `'labelName+=200'` | 200ms after a label |

**Timeline methods:**

```javascript
// Set values instantly (0-duration animation)
tl.set('.box', { opacity: 1 }, 0);

// Add labels for referencing positions
tl.label('halfway', '50%');

// Call a function at a position
tl.call(() => console.log('Reached this point'), '+=0');

// Sync a native WAAPI animation or another timer
tl.sync(existingAnimation, 0);

// Remove targets from the timeline
tl.remove('.box');

// Stretch timeline to a new total duration
tl.stretch(5000);

// Refresh function-based values
tl.refresh();
```

> [!TIP]
> Set `composition: false` on the timeline to disable internal composition rendering, which can improve performance for very complex timelines with many children.

### Stagger

Distribute values across multiple targets with fine-grained control:

```javascript
import { animate, stagger } from 'animejs';

// Basic stagger: 100ms between each target
animate('.items', {
  translateY: [-20, 0],
  opacity: [0, 1],
  delay: stagger(100),
});

// Range stagger: spread values from 0 to 500ms
animate('.items', {
  translateX: 250,
  delay: stagger([0, 500]),
});

// Stagger from center
animate('.items', {
  scale: [0, 1],
  delay: stagger(100, { from: 'center' }),
});

// Grid stagger (for grid layouts)
animate('.grid-items', {
  scale: [0, 1],
  delay: stagger(50, { grid: [10, 10], from: 'center', axis: 'y' }),
});

// Stagger with easing
animate('.items', {
  translateX: 250,
  delay: stagger(500, { ease: 'out(3)' }),
});
```

**Stagger `from` options:** `'first'` (default), `'center'`, `'last'`, `'random'`, or a numeric index.

**Stagger `axis` options (grid only):** `'x'`, `'y'`, or omit for radial distance.

### Scroll-Linked Animations

Use `ScrollObserver` to trigger or sync animations based on scroll position:

```javascript
import { animate, ScrollObserver } from 'animejs';

// Play/pause on enter/leave
const anim = animate('.element', {
  translateX: 250,
  autoplay: false,
});

const observer = new ScrollObserver({
  target: '.element',
  sync: 'play pause',     // Play on enter, pause on leave
});
observer.link(anim);

// Or pass the observer directly as autoplay
animate('.element', {
  translateX: 250,
  autoplay: new ScrollObserver({ target: '.element' }),
});

// Sync progress to scroll (scrubbing)
animate('.element', {
  translateX: [0, 500],
  autoplay: new ScrollObserver({
    target: '.element',
    sync: 'linear',        // Smooth linear sync
  }),
});

// Sync with easing (smooth scroll-linked)
animate('.element', {
  translateX: [0, 500],
  autoplay: new ScrollObserver({
    target: '.element',
    sync: 2,               // Numeric = smooth factor
  }),
});
```

**ScrollObserver sync modes:**

| Mode | Behavior |
|---|---|
| `'play pause'` | Play on enter, pause on leave |
| `'play reverse'` | Play on enter, reverse on leave |
| `'play restart'` | Play on enter, restart on leave |
| `'play pause reverse restart'` | Directional: enter-fwd, leave-fwd, enter-bwd, leave-bwd |
| `'linear'` | Direct 1:1 scroll-to-progress mapping |
| `true` or `1` | Smooth interpolated scroll sync (factor=1) |
| `2` | Smoother interpolation (higher = smoother) |
| Easing function | Custom easing applied to scroll progress |

**Threshold configuration:**

```javascript
new ScrollObserver({
  target: '.element',
  enter: 'top bottom',     // When top of element hits bottom of viewport
  leave: 'bottom top',     // When bottom of element hits top of viewport
  container: '.scroll-container', // Custom scroll container
  horizontal: true,        // Watch horizontal scroll
  repeat: true,            // Re-trigger on every enter/leave cycle
});
```

> [!WARNING]
> When using `sync: 'linear'` or numeric smooth modes, the linked animation's `duration` is ignored — progress is driven entirely by scroll position between the enter and leave thresholds.

**ScrollObserver callbacks:**

```javascript
new ScrollObserver({
  target: '.element',
  onEnter: (observer) => console.log('Entered'),
  onLeave: (observer) => console.log('Left'),
  onEnterForward: (observer) => {},
  onLeaveForward: (observer) => {},
  onEnterBackward: (observer) => {},
  onLeaveBackward: (observer) => {},
  onUpdate: (observer) => {},
  onResize: (observer) => {},
  onSyncComplete: (observer) => {},
});
```

### Draggable Elements

Create interactive drag interactions with inertia and snap:

```javascript
import { Draggable } from 'animejs';

// Basic draggable
const drag = new Draggable('.box');

// Constrained to a container with snap
const drag = new Draggable('.box', {
  container: '.container',
  snapX: 50,              // Snap to 50px grid
  snapY: 50,
  releaseEase: spring({ stiffness: 80, damping: 20 }),
});

// Single-axis drag
const drag = new Draggable('.slider-thumb', {
  y: false,               // Disable vertical dragging
  container: '.slider-track',
});

// Custom property mapping
const drag = new Draggable('.element', {
  x: { mapTo: 'translateX' },
  y: { mapTo: 'translateY' },
});
```

**Draggable callbacks:**

```javascript
new Draggable('.box', {
  onGrab: (d) => console.log('Grabbed'),
  onDrag: (d) => console.log(`Position: ${d.x}, ${d.y}`),
  onRelease: (d) => console.log('Released'),
  onUpdate: (d) => {},
  onSettle: (d) => console.log('Settled'),
  onSnap: (d) => console.log('Snapped'),
  onResize: (d) => {},
});
```

**Draggable properties:**

| Property | Type | Description |
|---|---|---|
| `x` | Boolean/Object | Horizontal drag config (`false` to disable) |
| `y` | Boolean/Object | Vertical drag config (`false` to disable) |
| `container` | Selector/Element | Bounding container |
| `trigger` | Selector/Element | Element that initiates the drag |
| `snapX` | Number/Array | Horizontal snap increment or snap points |
| `snapY` | Number/Array | Vertical snap increment or snap points |
| `containerFriction` | Number | Friction when hitting container edges |
| `releaseStiffness` | Number | Spring stiffness on release (default: 80) |
| `releaseDamping` | Number | Spring damping on release (default: 20) |
| `releaseMass` | Number | Spring mass on release (default: 1) |
| `releaseEase` | Easing/Spring | Release animation easing |
| `dragSpeed` | Number | Drag speed multiplier |
| `maxVelocity` | Number | Maximum release velocity |
| `scrollSpeed` | Number | Auto-scroll speed near edges |
| `scrollThreshold` | Number | Distance from edge to trigger auto-scroll |
| `cursor` | Boolean/Object | Cursor style management |

### Animatables

Create persistent, reactive animation bindings that update instantly when you call them:

```javascript
import { createAnimatable } from 'animejs';

const box = createAnimatable('.box', {
  translateX: 500,     // Duration in ms for the transition
  translateY: 500,
  rotate: 300,
});

// Animate to a new value
box.translateX(200);         // Animate translateX to 200 over 500ms
box.translateY(100, 200);    // Animate translateY to 100 over 200ms
box.rotate(45, 300, 'out(3)'); // With custom easing

// Read current value
const currentX = box.translateX(); // Returns current number
```

> [!NOTE]
> Animatables are ideal for interactive animations (mouse tracking, game loops) where you need to smoothly transition to new values on every frame without creating new animation instances.

### SVG Animations

#### Line Drawing

```javascript
import { animate } from 'animejs';
import { createDrawable } from 'animejs/svg';

const paths = createDrawable('svg path');

animate(paths, {
  draw: ['0 0', '0 1'],    // Draw from nothing to full path
  duration: 2000,
  ease: 'inOut(3)',
});
```

#### Motion Paths

```javascript
import { animate } from 'animejs';
import { createMotionPath } from 'animejs/svg';

const motionPath = createMotionPath('svg #motionPath');

animate('.element', {
  ...motionPath,            // Spreads translateX, translateY, rotate
  duration: 3000,
});
```

#### SVG Morphing

```javascript
import { animate } from 'animejs';
import { morphTo } from 'animejs/svg';

animate('svg #shape1', {
  d: morphTo('svg #shape2', 0.33),   // Morph with precision 0.33
  duration: 1000,
});
```

> [!IMPORTANT]
> `morphTo()` only works with `<path>`, `<polygon>`, and `<polyline>` SVG elements. Attempting to morph other SVG elements (like `<rect>` or `<circle>`) will throw an error.

### Text Splitting

Split text elements into individually animatable lines, words, and characters:

```javascript
import { TextSplitter } from 'animejs/text';
import { animate, stagger } from 'animejs';

const splitter = new TextSplitter('.text-element', {
  split: 'chars words lines',
});

// Animate characters
animate(splitter.chars, {
  opacity: [0, 1],
  translateY: [20, 0],
  delay: stagger(30),
});
```

**TextSplitter options:**

| Option | Type | Description |
|---|---|---|
| `split` | String | What to split: `'chars'`, `'words'`, `'lines'`, or combinations |
| `char` | Object | Template options for character wrappers |
| `word` | Object | Template options for word wrappers |
| `line` | Object | Template options for line wrappers |

Template options support `class`, `clone` (for clone-based reveals), and `wrap` (for overflow-clip containers).

### Layout Animations (FLIP)

Automatically animate DOM layout changes with `AutoLayout`:

```javascript
import { AutoLayout } from 'animejs/layout';

const layout = new AutoLayout('.container', {
  duration: 600,
  ease: 'out(3)',
  children: '.item',
  properties: ['backgroundColor', 'borderRadius'],
});

// Now any DOM mutation inside .container is automatically animated:
// - Reordering elements
// - Adding/removing elements
// - Resizing
// - Visibility changes
```

**AutoLayout options:**

| Option | Type | Description |
|---|---|---|
| `children` | Selector | Elements inside root to track |
| `properties` | Array | Extra CSS properties to animate during transitions |
| `duration` | Number/Function | Animation duration |
| `delay` | Number/Function | Animation delay |
| `ease` | Easing | Animation easing |
| `enterFrom` | Object | Properties for entering elements |
| `leaveTo` | Object | Properties for leaving elements |
| `swapAt` | Object | Properties at the swap midpoint |

### WAAPI Bridge

Use the native Web Animations API with anime.js's syntax and composition system:

```javascript
import { waapi } from 'animejs/waapi';

const anim = waapi('.box', {
  translateX: 250,
  opacity: [0, 1],
  duration: 1000,
  ease: 'out(3)',
});

// Playback controls work the same
anim.play();
anim.pause();
anim.reverse();
anim.seek(500);

// Sync with a timeline
import { createTimeline } from 'animejs';
const tl = createTimeline();
tl.sync(anim, 0);
```

> [!TIP]
> WAAPI animations run on the browser's compositor thread, which can provide smoother performance for simple transform and opacity animations. Use `waapi()` instead of `animate()` when you need maximum rendering performance and don't need additive composition or complex value types.

### Scopes

Group animations and clean them up together — essential for component-based frameworks (React, Vue, Angular):

```javascript
import { createScope, animate } from 'animejs';

const scope = createScope({ root: '#my-component' });

scope.add(() => {
  // All selectors are scoped to #my-component
  animate('.item', { translateX: 250 });
  animate('.title', { opacity: [0, 1] });

  // Return a cleanup function
  return () => console.log('Cleaned up');
});

// Define named methods
scope.add('enter', () => {
  animate('.item', { translateX: [0, 250] });
});

// Call named methods (executes within scope context)
scope.methods.enter();

// Clean up all animations in the scope
scope.revert();
```

**Scope with media queries:**

```javascript
const scope = createScope({
  root: '#app',
  mediaQueries: {
    isSmall: '(max-width: 640px)',
    prefersReduced: '(prefers-reduced-motion: reduce)',
  },
});

scope.add((self) => {
  if (self.matches.prefersReduced) return;
  animate('.hero', {
    translateY: self.matches.isSmall ? 50 : 200,
  });
});
// Scope automatically refreshes when media queries change
```

### Utility Functions

```javascript
import * as utils from 'animejs/utils';

// Get/set CSS and DOM values
utils.get('.box', 'translateX');        // Read current value
utils.set('.box', { opacity: 0.5 });   // Set without animation

// Number utilities (all are chainable)
utils.clamp(150, 0, 100);              // 100
utils.round(3.14159, 2);               // 3.14
utils.snap(27, 10);                    // 30
utils.snap(27, [0, 25, 50, 75, 100]);  // 25
utils.lerp(0, 100, 0.5);              // 50
utils.mapRange(5, 0, 10, 0, 100);     // 50
utils.wrap(370, 0, 360);              // 10
utils.degToRad(180);                   // 3.14159...
utils.radToDeg(Math.PI);              // 180
utils.roundPad(3.1, 3);               // "3.100"
utils.padStart(5, 3, '0');            // "005"
utils.damp(0, 100, 16, 0.1);         // Frame-rate independent damped lerp

// Chainable utilities
const transform = utils.clamp(0, 100).round(2).mapRange(0, 100, 0, 1);
transform(75.555); // 0.76

// Random numbers
utils.random(0, 100);                  // Random integer 0-100
utils.random(0, 1, 2);                // Random float 0-1, 2 decimal places
utils.randomPick(['a', 'b', 'c']);    // Random array element
utils.shuffle([1, 2, 3, 4, 5]);      // Fisher-Yates shuffle (in-place)

// Seeded random (deterministic)
const seeded = utils.createSeededRandom(42);
seeded(0, 100);  // Always produces the same sequence for seed 42
```

### Additive Animations (Composition: Blend)

Layer multiple animations on the same property:

```javascript
import { animate } from 'animejs';

animate('.box', {
  translateX: 100,
  composition: 'blend',
});

// This adds to the first, not replaces
animate('.box', {
  translateX: 50,
  composition: 'blend',
});
// Result: translateX animates toward 150 (100 + 50)
```

**Composition modes:**

| Mode | Behavior |
|---|---|
| `'replace'` | New animation overrides previous (default) |
| `'none'` | No composition tracking (fastest, no override) |
| `'blend'` | Additive — values stack on top of each other |

> [!NOTE]
> When targeting 1000+ elements, composition automatically switches to `'none'` for performance. You can override this by explicitly setting `composition: 'replace'` or `'blend'`.

## 6. Troubleshooting

### Diagnostic Decision Tree

1. **Animation not playing?** Check `autoplay` (default `true`), verify the target exists in the DOM before `animate()` runs, and check the browser console for the "No target found" warning.
2. **Animation looks wrong?** Inspect the `ease` and `duration` values. Verify units match (e.g., `'90deg'` not `90` for rotation angles).
3. **Performance issues?** Reduce `composition` to `'none'` for large target counts. Use `waapi()` for simple transform/opacity animations. Lower `engine.fps` to cap frame rate.
4. **Scroll animation not syncing?** Ensure the `target` element is in the DOM and within the scroll container's scrollable area. Check `enter`/`leave` threshold values.

### Error: `No target found. Make sure the element you're trying to animate is accessible before creating your animation.`

**Cause:** The CSS selector or target reference doesn't match any elements in the DOM at the time `animate()` is called.

**Solution:**
1. Verify the element exists: `document.querySelector('.your-selector')` should return an element
2. If using a framework, ensure the component has mounted before creating animations
3. Use `createScope()` to manage animation lifecycle with your component

**Prevention:** Always create animations inside `scope.add()` callbacks or after `DOMContentLoaded`.

### Error: `Can't morph a <rect> SVG element. Use <path>, <polygon> or <polyline>.`

**Cause:** `morphTo()` was called on an SVG element that isn't a `<path>`, `<polygon>`, or `<polyline>`.

**Solution:** Convert your SVG shapes to `<path>` elements using your SVG editor's "Convert to path" feature.

**Prevention:** Only use `morphTo()` with `<path>`, `<polygon>`, or `<polyline>` elements.

### Problem: Animation snaps to wrong values on restart

**Cause:** Previous animations on the same target/property weren't cleaned up. The composition system may hold stale tween siblings.

**Solution:**
1. Call `.revert()` on the previous animation before creating a new one
2. Use `composition: 'replace'` (the default) to automatically override previous animations
3. Use `createScope()` and call `scope.revert()` to clean up all animations at once

### Problem: Scroll-linked animation jumps or flickers

**Cause:** The linked animation's `autoplay` isn't set to the `ScrollObserver` instance, or the enter/leave thresholds overlap.

**Solution:**
```javascript
// Pass the observer directly as autoplay
animate('.element', {
  translateX: 250,
  autoplay: new ScrollObserver({ target: '.element', sync: 'linear' }),
});
```

**Prevention:** Always pass the `ScrollObserver` as the `autoplay` value rather than linking separately when using scroll sync.

### Problem: Spring animation never settles (GitHub #1118)

**Cause:** Spring physics with very low damping or very high stiffness can produce extremely long settling durations. The `onComplete` callback fires based on the perceived duration, not the mathematical settling point.

**Solution:** Increase `damping` (try 10–30) or decrease `stiffness`. Use `spring({ bounce: 0.3, duration: 800 })` for more predictable results.

### Problem: Draggable restricted to viewport with container (GitHub #1157)

**Cause:** When using a `container` parameter, the draggable bounds calculation uses the container's bounding rect. If the container has `overflow: visible`, the bounds may not match expectations.

**Solution:** Ensure the container element has explicit dimensions and `overflow: hidden` or `overflow: auto` if you want visual clipping to match the drag bounds.

### Problem: Jittery animations (GitHub #1112)

**Cause:** Frame rate fluctuations, especially during page load or when many animations run simultaneously.

**Solution:**
1. Cap the frame rate: `engine.fps = 60`
2. Use `waapi()` for simple animations to offload to the compositor
3. Reduce the number of simultaneously animating elements
4. Use `composition: 'none'` for large stagger groups

> [!CAUTION]
> Calling `.cancel()` on an animation permanently detaches its tweens from the composition system. The animation cannot be replayed after cancellation. Use `.pause()` if you need to resume later, or `.revert()` if you want to cleanly restore original styles.

## 7. FAQ

**Q: How do I use anime.js with React?**

Use `createScope()` with a ref to your component root. Call `scope.revert()` in the cleanup function:

```javascript
import { useRef, useEffect } from 'react';
import { createScope, animate } from 'animejs';

function MyComponent() {
  const root = useRef(null);
  const scope = useRef(null);

  useEffect(() => {
    scope.current = createScope({ root: root.current });
    scope.current.add(() => {
      animate('.item', { translateX: 250 });
    });
    return () => scope.current.revert();
  }, []);

  return <div ref={root}><div className="item">Hello</div></div>;
}
```

**Q: How do I use anime.js with Vue?**

```javascript
import { onMounted, onUnmounted, ref } from 'vue';
import { createScope, animate } from 'animejs';

const root = ref(null);
let scope;

onMounted(() => {
  scope = createScope({ root: root.value });
  scope.add(() => {
    animate('.item', { translateX: 250 });
  });
});

onUnmounted(() => scope?.revert());
```

**Q: Can I animate JavaScript objects (not DOM elements)?**

Yes. Pass an object as the target and animate any numeric property:

```javascript
const obj = { x: 0, y: 0, progress: 0 };
animate(obj, {
  x: 100,
  y: 200,
  progress: 1,
  duration: 1000,
  onUpdate: () => console.log(obj.x, obj.y, obj.progress),
});
```

**Q: How do I chain animations sequentially?**

Use a timeline:

```javascript
const tl = createTimeline();
tl.add('.first', { translateX: 100 })
  .add('.second', { translateY: 100 })
  .add('.third', { scale: 2 });
```

Or use promises:

```javascript
await animate('.first', { translateX: 100 });
await animate('.second', { translateY: 100 });
await animate('.third', { scale: 2 });
```

**Q: How do I pause all running animations?**

```javascript
import { engine } from 'animejs';
engine.pause();   // Pause everything
engine.resume();  // Resume everything
```

**Q: Does anime.js work in Node.js?**

Yes. The library detects the environment and works without a DOM. You can animate JavaScript objects in Node.js. DOM-related features (CSS, SVG, scroll, drag) require a browser.

**Q: What is the difference between `animate()` and `waapi()`?**

`animate()` uses anime.js's custom JavaScript rendering engine. `waapi()` delegates to the browser's native Web Animations API. Use `waapi()` for better performance on simple transform/opacity animations; use `animate()` when you need additive composition, complex value types, or SVG attribute animations.

**Q: How do I animate canvas or WebGL objects?**

Animate a plain JavaScript object and use `onUpdate` to sync values to your render loop:

```javascript
const state = { rotation: 0, scale: 1 };
animate(state, {
  rotation: Math.PI * 2,
  scale: 2,
  duration: 2000,
  onUpdate: () => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.save();
    ctx.rotate(state.rotation);
    ctx.scale(state.scale, state.scale);
    // Draw your scene
    ctx.restore();
  },
});
```

**Q: How do I remove specific targets from a running animation?**

Animations and timelines have no public `remove()` method on JSAnimation directly, but timelines expose `.remove(targets, propertyName)`:

```javascript
const tl = createTimeline();
tl.add('.items', { translateX: 250 });
tl.remove('.item-3');  // Remove .item-3 from the timeline
```

**Q: How do I handle `prefers-reduced-motion`?**

Use a scope with media queries:

```javascript
const scope = createScope({
  mediaQueries: { reduced: '(prefers-reduced-motion: reduce)' },
});
scope.add((self) => {
  if (self.matches.reduced) return;
  animate('.hero', { translateY: [50, 0], opacity: [0, 1] });
});
```

---

_Generated May 2026 from juliangarnier/anime at 3b369a4. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
