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:
npm install animejs
import { animate } from 'animejs';
animate('.box', {
translateX: 250,
rotate: '1turn',
duration: 800,
ease: 'out(3)',
});
// 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 globalanimeobject 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
npm install animejs
yarn
yarn add animejs
pnpm
pnpm add animejs
CDN (UMD)
<script src="https://cdn.jsdelivr.net/npm/animejs@4/dist/bundles/anime.umd.min.js"></script>
CDN (ES Module)
<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:
// 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"inpackage.json. CommonJS consumers should userequire('animejs')which resolves todist/modules/index.cjs.
Post-Install Verification
import { engine } from 'animejs';
console.log(engine.timeUnit); // 'ms'
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:
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
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: 1means 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:
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
// 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:
// 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 |
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. Anydurationparameter 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 |
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:
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
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():
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:
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:
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:
// 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: falseon 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:
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:
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:
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'sdurationis ignored — progress is driven entirely by scroll position between the enter and leave thresholds.
ScrollObserver callbacks:
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:
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:
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:
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
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
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
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:
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:
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:
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 ofanimate()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):
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:
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
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:
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 settingcomposition: 'replace'or'blend'.
6. Troubleshooting
Diagnostic Decision Tree
- Animation not playing? Check
autoplay(defaulttrue), verify the target exists in the DOM beforeanimate()runs, and check the browser console for the "No target found" warning. - Animation looks wrong? Inspect the
easeanddurationvalues. Verify units match (e.g.,'90deg'not90for rotation angles). - Performance issues? Reduce
compositionto'none'for large target counts. Usewaapi()for simple transform/opacity animations. Lowerengine.fpsto cap frame rate. - Scroll animation not syncing? Ensure the
targetelement is in the DOM and within the scroll container's scrollable area. Checkenter/leavethreshold 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:
- Verify the element exists:
document.querySelector('.your-selector')should return an element - If using a framework, ensure the component has mounted before creating animations
- 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:
- Call
.revert()on the previous animation before creating a new one - Use
composition: 'replace'(the default) to automatically override previous animations - Use
createScope()and callscope.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:
// 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:
- Cap the frame rate:
engine.fps = 60 - Use
waapi()for simple animations to offload to the compositor - Reduce the number of simultaneously animating elements
- 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:
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?
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:
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:
const tl = createTimeline();
tl.add('.first', { translateX: 100 })
.add('.second', { translateY: 100 })
.add('.third', { scale: 2 });
Or use promises:
await animate('.first', { translateX: 100 });
await animate('.second', { translateY: 100 });
await animate('.third', { scale: 2 });
Q: How do I pause all running animations?
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:
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):
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:
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] });
});