| name | animejs |
| description | Write correct anime.js v4 animations with the right imports, API patterns, easing, timelines, and advanced features. Use this skill whenever the user asks to animate DOM elements, create CSS/SVG animations, build scroll-linked effects, add drag interactions, choreograph timeline sequences, or write any JavaScript animation code — even if they don't mention 'anime.js' by name. Also triggers when the user mentions 'animejs', 'anime.js', 'createTimeline', 'createDraggable', 'stagger', 'spring easing', or imports from the 'animejs' package. If the user wants smooth, performant web animations and isn't already using GSAP or Framer Motion, this skill applies. |
Anime.js v4 Animation Skill
Anime.js v4 is a modular JavaScript animation engine (10KB full / 3KB WAAPI-only). This skill ensures you write correct v4 syntax — the API changed significantly from v3.
Installation
npm install animejs
CDN (ESM):
<script type="module">
import { animate } from 'https://cdn.jsdelivr.net/npm/animejs/+esm';
</script>
Core Concept: Named Imports
v4 uses named exports, not a default anime object. Import only what you need:
import { animate, stagger, createTimeline, utils } from 'animejs';
Subpath imports for smaller bundles:
import { animate } from 'animejs/animation';
import { createTimeline } from 'animejs/timeline';
import { createDraggable } from 'animejs/draggable';
import { onScroll } from 'animejs/events';
import { stagger, utils } from 'animejs/utils';
import { waapi } from 'animejs/waapi';
Quick Reference: animate()
animate(targets, parameters);
Targets: CSS selectors, DOM elements, NodeLists, JS objects, or arrays of these.
Parameters combine animatable properties + playback settings + callbacks:
animate('.box', {
translateX: 250,
opacity: [0, 1],
scale: { from: 0.5, to: 1 },
backgroundColor: '#FF0000',
duration: 800,
delay: stagger(100),
ease: 'outExpo',
loop: 2,
alternate: true,
onComplete: () => console.log('done'),
});
Animatable Properties
| Category | Examples |
|---|
| CSS | opacity, width, height, backgroundColor, borderRadius |
| Transforms | translateX, translateY, rotate, scale, skewX |
| CSS Variables | '--my-var': 100 |
| SVG Attributes | cx, r, points, d (via morphTo) |
| HTML Attributes | value, data-count |
| JS Object Props | Any numeric property on a plain object |
Value Formats
translateX: 250,
translateX: '10rem',
translateX: '+=100',
translateX: '-=50px',
opacity: [0, 1],
scale: { from: 0.5, to: 1.5 },
rotate: '1turn',
Function-Based Values
animate('.item', {
translateX: (el, i, total) => i * 50,
delay: (el, i) => i * 100,
});
Keyframes (Per-Property)
animate('.ball', {
y: [
{ to: '-2.75rem', ease: 'outExpo', duration: 600 },
{ to: 0, ease: 'outBounce', duration: 800, delay: 100 },
],
});
Playback Settings
| Setting | Default | Description |
|---|
duration | 1000 | ms |
delay | 0 | ms before start |
loop | 0 | repetitions (0 = play once, true = infinite) |
loopDelay | 0 | ms between loops |
alternate | false | reverse on each loop |
reversed | false | play backward |
autoplay | true | start immediately |
playbackRate | 1 | speed multiplier |
frameRate | null | cap FPS |
Callbacks
animate(el, {
translateX: 200,
onBegin: (anim) => {},
onUpdate: (anim) => {},
onRender: (anim) => {},
onLoop: (anim) => {},
onPause: (anim) => {},
onComplete: (anim) => {},
});
animate(el, { x: 100 }).then(() => console.log('done'));
Playback Methods
const anim = animate(el, { x: 200 });
anim.play();
anim.pause();
anim.resume();
anim.reverse();
anim.restart();
anim.seek(500);
anim.complete();
anim.cancel();
anim.revert();
Easing
v4 uses ease (not easing). Names are shortened (no ease prefix):
ease: 'outQuad'
ease: 'inOutExpo'
ease: 'linear'
ease: 'out(3)'
ease: 'inOut(2)'
All built-in easings: linear, in, out, inOut, outIn, inQuad, outQuad, inOutQuad, inCubic, outCubic, inOutCubic, inQuart, outQuart, inOutQuart, inQuint, outQuint, inOutQuint, inSine, outSine, inOutSine, inCirc, outCirc, inOutCirc, inExpo, outExpo, inOutExpo, inBounce, outBounce, inOutBounce, inBack, outBack, inOutBack, inElastic, outElastic, inOutElastic.
Cubic Bezier
import { cubicBezier } from 'animejs';
ease: cubicBezier(0.7, 0.1, 0.5, 0.9)
Spring Physics
import { createSpring } from 'animejs';
ease: createSpring({ mass: 1, stiffness: 100, damping: 10, velocity: 0 })
Presets: spring() (default), spring({ stiffness: 200, damping: 8 }) (snappy), spring({ stiffness: 80, damping: 5 }) (bouncy).
Steps
ease: 'steps(5)'
Custom Easing Function
ease: t => 1 - Math.sqrt(1 - t * t)
Stagger
import { stagger } from 'animejs';
delay: stagger(100)
delay: stagger(100, { start: 500 })
delay: stagger(100, { from: 'center' })
delay: stagger(100, { from: 'last' })
delay: stagger(100, { reversed: true })
delay: stagger([0, 200])
delay: stagger(100, { grid: [14, 5] })
delay: stagger(100, { grid: [14, 5], from: 'center', ease: 'inQuad' })
Timeline
import { createTimeline } from 'animejs';
const tl = createTimeline({
defaults: { duration: 600, ease: 'outExpo' },
loop: true,
alternate: true,
});
tl.add('.box-1', { translateX: 250 })
.add('.box-2', { translateX: 250 }, 200)
.add('.box-3', { translateX: 250 }, '+=100')
.add('.box-4', { translateX: 250 }, '-=300')
.add('.box-5', { translateX: 250 }, '<')
.add('.box-6', { translateX: 250 }, '<+=200');
Labels
tl.label('intro')
.add('.title', { opacity: [0, 1] })
.label('content', '+=500')
.add('.body', { opacity: [0, 1] }, 'content');
Timeline Callbacks
tl.call(() => console.log('midpoint'), 1500);
Scroll-Linked Animations
import { animate, onScroll } from 'animejs';
animate('.card', {
opacity: [0, 1],
translateY: [50, 0],
autoplay: onScroll({
target: '.card',
enter: 'bottom',
leave: 'top',
}),
});
animate('.progress-bar', {
scaleX: [0, 1],
autoplay: onScroll({
target: '.section',
sync: 'playback',
}),
});
SVG Utilities
import { animate, svg } from 'animejs';
const drawable = svg.createDrawable('path.line');
animate(drawable, { draw: '0 1', duration: 1500 });
animate('path#shape', {
d: svg.morphTo('path#target-shape'),
duration: 1000,
});
const path = svg.createMotionPath('path#track');
animate('.mover', {
...path,
duration: 2000,
});
Draggable
import { createDraggable } from 'animejs';
const drag = createDraggable('.card', {
container: '.bounds',
releaseEase: createSpring({ stiffness: 100, damping: 15 }),
cursor: { onHover: 'grab', onGrab: 'grabbing' },
snap: { x: 50, y: 50 },
onDrag: (draggable) => {},
onRelease: (draggable) => {},
onSettle: (draggable) => {},
});
Animatable (High-Frequency Updates)
For values that change every frame (mouse tracking, game loops), use createAnimatable instead of calling animate() repeatedly:
import { createAnimatable, utils } from 'animejs';
const box = createAnimatable('.box', {
x: 500,
y: 500,
ease: 'out(3)',
});
window.addEventListener('mousemove', (e) => {
box.x(utils.clamp(e.clientX - cx, -limit, limit));
box.y(utils.clamp(e.clientY - cy, -limit, limit));
});
Text Animation
import { animate, splitText, stagger } from 'animejs';
const { chars, words, lines } = splitText('h1', {
chars: true,
words: true,
lines: true,
});
animate(chars, {
opacity: [0, 1],
translateY: ['1em', 0],
delay: stagger(30),
ease: 'outExpo',
});
Layout Animations
import { createLayout, stagger } from 'animejs';
const layout = createLayout('.grid-container');
layout.update(({ root }) => {
root.classList.toggle('column-layout');
}, {
duration: 800,
delay: stagger(50),
ease: 'outExpo',
});
WAAPI (Lightweight 3KB Alternative)
For simpler animations that don't need JS engine features, use the WAAPI wrapper for hardware-accelerated performance:
import { waapi, stagger, splitText } from 'animejs';
waapi.animate('.box', {
translate: '0 -2rem',
opacity: [0, 1],
delay: stagger(100),
duration: 600,
ease: 'inOut(2)',
});
Utilities
import { utils } from 'animejs';
utils.get(el, 'translateX');
utils.set(el, { opacity: 0.5 });
utils.remove(el);
utils.random(50, 100);
utils.clamp(val, min, max);
utils.lerp(start, end, amount);
utils.mapRange(inLow, inHigh, outLow, outHigh, val);
utils.snap(val, increment);
utils.round(val, decimals);
utils.wrap(val, min, max);
React Integration
import { useRef, useEffect } from 'react';
import { createScope, animate, stagger } from 'animejs';
function AnimatedList({ items }) {
const root = useRef(null);
const scope = useRef(null);
useEffect(() => {
scope.current = createScope({ root: root.current })
.add(() => {
animate('.item', {
opacity: [0, 1],
translateY: [20, 0],
delay: stagger(50),
ease: 'outExpo',
});
});
return () => scope.current.revert();
}, []);
return (
<ul ref={root}>
{items.map(item => <li key={item.id} className="item">{item.name}</li>)}
</ul>
);
}
createScope scopes CSS selectors to the root element and provides batch revert() on cleanup — essential for React's strict mode and unmount lifecycle.
Animation Composition
v4 handles overlapping animations on the same property:
animate(el, { x: 100 });
animate(el, { x: 200 });
animate(el, { x: 100, composition: 'add' });
animate(el, { x: 50, composition: 'add' });
animate(el, { x: 100, composition: 'none' });
Common Pitfalls
ease not easing — v4 renamed it
loop: 1 means repeat once (2 total plays), not "play once" like v3
- No default
anime object — use named imports
direction is gone — use alternate: true and reversed: true
- Easing names dropped
ease prefix — outQuad not easeOutQuad
- Spring syntax changed — use
createSpring({...}) not 'spring(1,80,10,0)'
- SVG helpers moved —
svg.createMotionPath() not anime.path()
onRender replaces change, onLoop replaces loopBegin/loopComplete
play() always goes forward — use resume() to continue previous direction
Reference Files
For detailed API signatures and parameters, read references/api-reference.md.
For common animation recipes and patterns, read references/examples.md.