anime.js v4 Animation
You are an expert in anime.js v4 (latest: v4.3.5), the lightweight JavaScript animation library. Generate production-ready animation code using the v4 API exclusively. Never use v3 syntax.
CRITICAL: v4 API Only
anime.js v4 introduced breaking changes from v3. ALWAYS use v4 syntax:
| NEVER use (v3) | ALWAYS use (v4) |
|---|---|
anime({ targets: '.el', ... }) | animate('.el', { ... }) |
anime.timeline() | createTimeline() |
anime.stagger() | stagger() |
easing: 'easeOutQuad' | ease: 'outQuad' |
begin: fn | onBegin: fn |
change: fn | onUpdate: fn |
complete: fn | onComplete: fn |
direction: 'alternate' | alternate: true |
direction: 'reverse' | reversed: true |
endDelay | loopDelay |
anime.path() | svg.createMotionPath() |
anime.setDashoffset() | svg.createDrawable() |
animation.finished.then() | animation.then() |
Installation & Setup
CDN:
<script src="https://cdn.jsdelivr.net/npm/animejs@4/lib/anime.iife.min.js"></script>
npm:
npm install animejs
ES Module imports (recommended):
import { animate, stagger, createTimeline } from 'animejs';
// Additional features — import only what you need:
import { createAnimatable } from 'animejs';
import { createDraggable } from 'animejs';
import { createLayout } from 'animejs';
import { onScroll } from 'animejs';
import { waapi } from 'animejs';
import { svg } from 'animejs';
import { splitText } from 'animejs';
import { engine, utils } from 'animejs';
Subpath imports (tree-shaking):
import { animate } from 'animejs/animation';
import { createTimeline } from 'animejs/timeline';
import { createDraggable } from 'animejs/draggable';
import { onScroll } from 'animejs/events';
import { splitText } from 'animejs/text';
Core: animate(targets, parameters)
Targets
// CSS selector
animate('.box', { ... });
// DOM element
animate(document.querySelector('#hero'), { ... });
// Multiple elements (NodeList, Array)
animate(document.querySelectorAll('.card'), { ... });
// JavaScript object (animate numeric properties)
const obj = { progress: 0 };
animate(obj, { progress: 100, onUpdate: () => console.log(obj.progress) });
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
duration | number | 1000 | Milliseconds |
delay | number/function | 0 | Delay before start |
ease | string/function | 'outQuad' | Easing function |
loop | number/boolean | false | true = infinite, number = repeat count |
loopDelay | number | 0 | Delay between loops |
alternate | boolean | false | Reverse direction each loop |
reversed | boolean | false | Play backwards |
autoplay | boolean | true | Start immediately |
frameRate | number | - | Limit FPS |
playbackRate | number | 1 | Speed multiplier (2 = double speed) |
composition | string | 'replace' | 'replace', 'add', 'blend' |
modifier | function | - | Transform animated value before applying |
Callbacks
animate('.el', {
translateX: 250,
onBegin: (anim) => {}, // When animation starts (after delay)
onUpdate: (anim) => {}, // Every frame
onComplete: (anim) => {}, // When animation finishes
onLoop: (anim) => {}, // After each loop iteration
onPause: (anim) => {}, // When paused
}).then((anim) => {}); // Promise-based completion
Playback Controls
const anim = animate('.el', { translateX: 250, autoplay: false });
anim.play(); // Play forward
anim.pause(); // Pause
anim.resume(); // Resume from current position and direction
anim.reverse(); // Reverse direction
anim.restart(); // Restart from beginning
anim.seek(500); // Jump to 500ms
anim.reset(); // Reset to initial values
anim.complete(); // Jump to end
anim.cancel(); // Cancel and clean up
anim.revert(); // Revert targets to original values
Animatable Properties
animate('.el', {
// CSS transforms
translateX: 250,
translateY: 100,
rotate: '1turn',
scale: 1.5,
skewX: '15deg',
// CSS properties
opacity: [0, 1],
backgroundColor: '#FF0000',
borderRadius: '50%',
width: '200px',
// CSS variables
'--custom-prop': 100,
// Relative values
translateX: '+=100',
rotate: '-=45deg',
// From-to syntax
opacity: { from: 0, to: 1 },
translateY: [50, 0], // [from, to]
// Per-property parameters
translateX: { to: 250, duration: 800, ease: 'outExpo' },
opacity: { to: 1, duration: 400, delay: 200 },
// Function-based values (per target)
translateX: (target, index, total) => index * 50,
delay: (target, index) => index * 100,
});
Keyframes
Property-level (array of values):
animate('.el', {
translateX: [0, 100, 50, 200],
duration: 2000,
});
Property-level (array of objects):
animate('.el', {
translateX: [
{ to: 100, duration: 500, ease: 'outQuad' },
{ to: 50, duration: 300 },
{ to: 200, duration: 700, ease: 'inOutCubic' },
],
});
Percentage-based:
animate('.el', {
keyframes: {
'0%': { translateX: 0, opacity: 0 },
'40%': { translateX: 100, opacity: 1 },
'100%': { translateX: 250, opacity: 0.5 },
},
duration: 2000,
});
Stagger
Distribute values or delays across multiple targets.
import { animate, stagger } from 'animejs';
// Basic delay stagger: each element starts 100ms after the previous
animate('.card', {
opacity: [0, 1],
translateY: [30, 0],
delay: stagger(100),
duration: 500,
ease: 'outCubic',
});
// From center outward
animate('.item', { scale: [0, 1], delay: stagger(80, { from: 'center' }) });
// From edges inward
animate('.item', { scale: [0, 1], delay: stagger(80, { from: 'edges' }) });
// From specific index
animate('.item', { scale: [0, 1], delay: stagger(80, { from: 3 }) });
// Range distribution: values spread evenly between 0 and 200
animate('.item', { translateX: stagger([0, 200]) });
// Grid stagger (rows x cols)
animate('.grid-item', {
scale: [0, 1],
delay: stagger(50, { grid: [4, 6], from: 'center', axis: 'y' }),
});
// Eased stagger distribution
animate('.item', { opacity: [0, 1], delay: stagger(300, { ease: 'inOutQuad' }) });
Timelines
Sequence and synchronize multiple animations.
import { createTimeline, stagger } from 'animejs';
const tl = createTimeline({
defaults: { duration: 600, ease: 'outQuad' },
});
// Animations chain sequentially by default
tl.add('.header', { opacity: [0, 1], translateY: [-30, 0] })
.add('.subtitle', { opacity: [0, 1], translateY: [20, 0] }, '-=300') // Overlap by 300ms
.add('.cta-btn', { opacity: [0, 1], scale: [0.8, 1] }, '+=100') // 100ms gap
.add('.cards', {
opacity: [0, 1],
translateY: [40, 0],
delay: stagger(80),
}, '-=200');
// Absolute positioning (ms from timeline start)
tl.add('.bg', { opacity: [0, 0.5] }, 0); // Starts at 0ms
// Timeline controls
tl.play();
tl.pause();
tl.seek(1000);
tl.reverse();
tl.restart();
Text Splitting
Split text into lines, words, or characters for per-element animation.
import { splitText, animate, stagger } from 'animejs';
// Split heading into characters
const splitter = splitText('.hero-title', {
chars: true, // Split into characters
// words: true, // Split into words
// lines: true, // Split into lines
accessible: true, // Maintain screen reader accessibility
});
// Animate each character
animate(splitter.chars, {
opacity: [0, 1],
translateY: [20, 0],
delay: stagger(30),
duration: 400,
ease: 'outCubic',
});
// Revert DOM to original text when done
// splitter.revert();
Scroll Observer
Trigger and synchronize animations with scroll position.
import { animate, onScroll } from 'animejs';
// Trigger animation when element enters viewport
onScroll('.section', {
onEnter: () => {
animate('.section .content', {
opacity: [0, 1],
translateY: [40, 0],
duration: 800,
ease: 'outQuad',
});
},
});
// Directional callbacks
onScroll('.element', {
onEnterForward: () => {}, // Scrolling down into view
onEnterBackward: () => {}, // Scrolling up into view
onLeaveForward: () => {}, // Scrolling down out of view
onLeaveBackward: () => {}, // Scrolling up out of view
});
// Sync animation progress to scroll position
const anim = animate('.parallax-bg', {
translateY: [-100, 100],
autoplay: false,
});
onScroll('.parallax-section', {
sync: 'playback', // Links scroll position to animation progress
link: anim,
});
// Thresholds
onScroll('.el', {
enter: 'top bottom', // When top of el crosses bottom of viewport
leave: 'bottom top', // When bottom of el crosses top of viewport
});
WAAPI (Web Animation API)
Lightweight 3KB alternative using browser-native Web Animation API. Hardware-accelerated. Use for simple animations where performance is critical.
import { waapi } from 'animejs';
const anim = waapi.animate('.el', {
translateX: 250,
opacity: [0, 1],
duration: 600,
ease: 'outQuad',
});
When to use WAAPI vs full animate():
- WAAPI: Simple CSS transforms/opacity, high-performance needs, many simultaneous elements
- Full
animate(): JS object animation, complex keyframes,onUpdatecallback, modifier functions, SVG features
Animatable
Reactive value animation — ideal for cursor tracking, interactive UIs, and frequently changing values.
import { createAnimatable } from 'animejs';
const follower = createAnimatable('.cursor-follower', {
x: { duration: 300, ease: 'outQuad' },
y: { duration: 300, ease: 'outQuad' },
});
document.addEventListener('mousemove', (e) => {
follower.x(e.clientX); // Setter: animates to value
follower.y(e.clientY);
});
// Getter
const currentX = follower.x();
Layout Animations
Animate layout changes that are normally impossible (CSS display, grid, flex, DOM order).
import { createLayout } from 'animejs';
const layout = createLayout('.container', {
duration: 500,
ease: 'outQuad',
// Enter/exit states for added/removed elements
enterFrom: { opacity: 0, scale: 0.8 },
leaveTo: { opacity: 0, scale: 0.8 },
});
// Animate any layout change
layout.update(() => {
// Toggle class, change display, reorder DOM, etc.
container.classList.toggle('grid-view');
});
// Or manual record → animate pattern
layout.record();
// ... make DOM changes ...
layout.animate();
Draggable
Add drag interaction with physics.
import { createDraggable } from 'animejs';
const draggable = createDraggable('.card', {
x: { snap: 100 }, // Snap to 100px grid on x-axis
y: { snap: 100 },
friction: 0.5, // Drag friction
mass: 1, // Physics mass
stiffness: 100, // Spring stiffness
damping: 10, // Spring damping
onGrab: (self) => {},
onDrag: (self) => {},
onRelease: (self) => {},
onSnap: (self) => {},
onSettle: (self) => {},
});
// Constrain to container
const draggable = createDraggable('.el', {
container: '.bounds',
x: true,
y: true,
});
SVG Features
import { animate, svg } from 'animejs';
// Line drawing (animate stroke)
const drawable = svg.createDrawable('.svg-path');
animate(drawable, { draw: [0, 1], duration: 1500, ease: 'inOutQuad' });
// Shape morphing
animate('.shape path', {
d: svg.morphTo('.target-shape path'),
duration: 1000,
ease: 'inOutQuad',
});
// Motion path (move element along SVG path)
const motionPath = svg.createMotionPath('.motion-path');
animate('.moving-element', {
...motionPath, // Spread the path data
duration: 3000,
ease: 'linear',
loop: true,
});
Easings
| Family | In | Out | InOut |
|---|---|---|---|
| Quad | inQuad | outQuad | inOutQuad |
| Cubic | inCubic | outCubic | inOutCubic |
| Quart | inQuart | outQuart | inOutQuart |
| Quint | inQuint | outQuint | inOutQuint |
| Sine | inSine | outSine | inOutSine |
| Expo | inExpo | outExpo | inOutExpo |
| Circ | inCirc | outCirc | inOutCirc |
| Back | inBack | outBack | inOutBack |
| Elastic | inElastic | outElastic | inOutElastic |
Custom easings:
ease: 'cubicBezier(0.25, 0.1, 0.25, 1.0)' // CSS cubic-bezier
ease: 'spring(1, 100, 10, 0)' // spring(mass, stiffness, damping, velocity)
ease: 'steps(5)' // Stepped animation
ease: 'linear' // Constant speed
When to use which:
- Entrances:
outQuad,outCubic,outExpo(fast start, slow end) - Exits:
inQuad,inCubic(slow start, fast end) - State changes:
inOutQuad,inOutCubic(smooth both ends) - Bouncy/playful:
outBack,outElastic,spring() - Continuous/looping:
linear
For the complete easing catalog with motion descriptions and presets, see references/easing-reference.md.
Engine Configuration
import { engine } from 'animejs';
engine.timeUnit = 'ms'; // 'ms' or 's'
engine.speed = 1; // Global speed multiplier
engine.fps = 60; // Max frames per second
engine.precision = 4; // Decimal precision
engine.pauseOnDocumentHidden = true; // Pause when tab hidden
Utilities
import { utils } from 'animejs';
utils.get('.el', 'translateX'); // Get current value
utils.set('.el', { opacity: 0.5 }); // Set without animating
utils.random(0, 100); // Random number
utils.clamp(value, 0, 100); // Clamp to range
utils.snap(value, 10); // Snap to nearest 10
utils.mapRange(value, 0, 1, 0, 100); // Remap range
utils.lerp(start, end, 0.5); // Linear interpolation
Selector shorthand:
import { $ } from 'animejs';
const elements = $('.card'); // Like querySelectorAll
Common Patterns
Fade in on load
animate('.element', {
opacity: [0, 1],
translateY: [20, 0],
duration: 600,
ease: 'outQuad',
});
Staggered list entrance
animate('.list-item', {
opacity: [0, 1],
translateY: [30, 0],
delay: stagger(80),
duration: 500,
ease: 'outCubic',
});
Hover scale effect
document.querySelectorAll('.btn').forEach(btn => {
btn.addEventListener('mouseenter', () =>
animate(btn, { scale: 1.05, duration: 200, ease: 'outQuad' })
);
btn.addEventListener('mouseleave', () =>
animate(btn, { scale: 1, duration: 200, ease: 'outQuad' })
);
});
Loading spinner
animate('.spinner', {
rotate: '1turn',
duration: 800,
loop: true,
ease: 'linear',
});
Animated counter
const counter = { value: 0 };
animate(counter, {
value: 2500,
duration: 2000,
ease: 'outExpo',
onUpdate: () => {
document.querySelector('.count').textContent = Math.round(counter.value);
},
});
Scroll-triggered reveal
onScroll('.reveal-section', {
onEnter: () => {
animate('.reveal-section .item', {
opacity: [0, 1],
translateY: [40, 0],
delay: stagger(100),
duration: 600,
ease: 'outCubic',
});
},
});
Text reveal
const splitter = splitText('.headline', { chars: true, accessible: true });
animate(splitter.chars, {
opacity: [0, 1],
translateY: [20, 0],
delay: stagger(25),
duration: 400,
ease: 'outCubic',
});
Draggable card
createDraggable('.drag-card', {
container: '.card-area',
x: true,
y: true,
friction: 0.7,
onRelease: (self) => {
// Snap back or process drop
},
});
For complete website animation patterns (hero sections, navigation effects, parallax, modals, carousels, etc.), see references/website-animation-patterns.md.
v3 to v4 Complete Migration Reference
| v3 (NEVER use) | v4 (ALWAYS use) |
|---|---|
anime({ targets: '.el', ... }) | animate('.el', { ... }) |
anime.timeline({ ... }) | createTimeline({ ... }) |
anime.stagger(100) | stagger(100) |
easing: 'easeOutQuad' | ease: 'outQuad' |
easing: 'easeInOutExpo' | ease: 'inOutExpo' |
begin: fn | onBegin: fn |
change: fn | onUpdate: fn |
complete: fn | onComplete: fn |
changeBegin: fn | Removed — use onBegin |
changeComplete: fn | Removed — use onComplete |
direction: 'alternate' | alternate: true |
direction: 'reverse' | reversed: true |
endDelay: 500 | loopDelay: 500 |
anime.remove(targets) | anim.revert() |
anime.path('.path') | svg.createMotionPath('.path') |
anime.setDashoffset(el) | svg.createDrawable(el) |
animation.finished.then(fn) | animation.then(fn) |
value: 100 (in keyframe objects) | to: 100 |
import anime from 'animejs' | import { animate } from 'animejs' |