🎯

animejs-v4

🎯Skill

from matthewharwood/fantasy-phonics

VibeIndex|
What it does

animejs-v4 skill from matthewharwood/fantasy-phonics

animejs-v4

Installation

Install skill:
npx skills add https://github.com/matthewharwood/fantasy-phonics --skill animejs-v4
1
AddedJan 29, 2026

Skill Details

SKILL.md

Anime.js 4.0 animations for Web Components β€” drag-drop, click feedback, swaps, cancelable motion. Use when adding animations, drag interactions, visual feedback, or motion to custom elements. Combines with web-components-architecture for lifecycle cleanup.

Overview

# Anime.js 4.0 for Web Components

Installation

```bash

npm i animejs

```

```javascript

import { animate, createTimeline, stagger, createSpring, createDraggable } from 'animejs';

import { createDrawable, createMotionPath, morphTo, splitText, onScroll } from 'animejs';

```

---

Core API

Basic Animation

```javascript

animate('.el', {

x: 250, y: 100, rotate: 90, scale: 1.5, opacity: 0.5,

backgroundColor: '#FFF',

duration: 1000, delay: 200, ease: 'outQuad',

loop: true, alternate: true

});

```

Per-Property Control

```javascript

animate('.el', {

x: { from: -100, to: 100, duration: 800 },

rotate: { from: 0, to: 360, ease: 'inOutCirc' },

scale: { to: 1.5, delay: 200 }

});

```

Keyframes

```javascript

animate('.el', { y: [0, -50, 0], scale: [1, 1.2, 1], duration: 1000 });

```

Stagger

```javascript

animate('.items', {

y: -20, opacity: [0, 1],

delay: stagger(100), // Sequential

delay: stagger(100, { from: 'center' }), // From center

delay: stagger(50, { grid: [5, 5] }) // Grid layout

});

```

Function-Based Values

```javascript

animate('.items', {

x: (el, i, total) => i * 50,

rotate: (el, i) => i % 2 === 0 ? 45 : -45

});

```

---

Playback Control

```javascript

const anim = animate('.el', { x: 100, autoplay: false });

anim.play();

anim.pause();

anim.reverse();

anim.restart();

anim.seek(500); // Seek to 500ms

anim.seek('50%'); // Seek to 50%

```

| Method | Behavior |

|--------|----------|

| cancel() | Stop immediately, keep current inline styles |

| revert() | Stop and remove all inline styles |

| complete() | Jump to final values immediately |

---

Callbacks & Promises

```javascript

animate('.el', {

x: 100,

onBegin: (anim) => {},

onUpdate: (anim) => {},

onComplete: (anim) => {}

});

await animate('.el', { x: 100 }); // Promise-based

```

---

Timeline

```javascript

const tl = createTimeline({ loop: true, alternate: true });

tl.add('.box1', { x: 100 })

.add('.box2', { y: 50 }, '-=200') // 200ms before previous ends

.add('.box3', { scale: 2 }, '+=100') // 100ms after previous

.add('.box4', { rotate: 90 }, 500); // At 500ms absolute

tl.label('myLabel', 1000);

tl.call(() => console.log('done'), 2000);

```

---

Easings & Springs

Built-in: linear, in, out, inOut, outIn (with power: out(3)).

Named: inQuad, outQuad, inOutQuad, inCubic, outExpo, inOutElastic, outBounce, outBack.

```javascript

// Spring physics

animate('.el', { x: 100, ease: createSpring({ stiffness: 400, damping: 25 }) });

```

Spring Presets

```javascript

const springs = {

click: createSpring({ stiffness: 600, damping: 30 }),

move: createSpring({ stiffness: 300, damping: 25 }),

drop: createSpring({ stiffness: 400, damping: 20 }),

settle: createSpring({ stiffness: 200, damping: 25 }),

bounce: createSpring({ stiffness: 500, damping: 10 })

};

```

---

Web Component Integration

Animation Manager Pattern

Always cancel existing animations before starting new ones. Use a WeakMap to track animations per element without memory leaks.

```javascript

class AnimationManager {

#anims = new WeakMap();

animate(el, props, key = 'main') {

const map = this.#anims.get(el) || {};

map[key]?.cancel(); // Cancel existing

const anim = animate(el, props);

map[key] = anim;

this.#anims.set(el, map);

return anim;

}

cancel(el, key = 'main') { this.#anims.get(el)?.[key]?.cancel(); }

revert(el, key = 'main') { this.#anims.get(el)?.[key]?.revert(); }

}

```

Component Lifecycle

Follows web-components-architecture skill β€” use disconnectedCallback for cleanup:

```javascript

class AnimatedCard extends HTMLElement {

#anim = null;

animate(props) {

this.#anim?.cancel();

this.#anim = animate(this, props);

return this.#anim;

}

disconnectedCallback() {

this.#anim?.revert(); // Clean up inline styles

}

}

```

---

Drag-and-Drop Animations

Drag Start (Lift)

```javascript

function animateDragStart(card) {

return animate(card, {

scale: 1.12, rotate: 8,

boxShadow: '0 20px 50px rgba(0,0,0,0.3)',

duration: 150, ease: 'out(3)'

});

}

```

During Drag β€” Direct Transform (60fps)

Do NOT use animate() for pointer tracking. Set transform directly for smooth motion:

```javascript

function updateDragPosition(el, x, y) {

el.style.transform = translate(${x}px, ${y}px) scale(1.12) rotate(8deg);

}

```

Drop Animation

```javascript

function animateDrop(card) {

return animate(card, {

x: 0, y: 0,

scale: [1.12, 0.95, 1],

rotate: [8, -2, 0],

boxShadow: '0 2px 8px rgba(0,0,0,0.1)',

duration: 350, ease: 'outBack'

});

}

```

Return to Origin

```javascript

function animateReturn(card) {

return animate(card, {

x: 0, y: 0, scale: 1, rotate: 0,

duration: 400, ease: 'outBack'

});

}

```

Card Swap (FLIP Pattern)

```javascript

async function animateSwap(cardA, cardB, slotA, slotB) {

const rectA = cardA.getBoundingClientRect();

const rectB = cardB.getBoundingClientRect();

// DOM swap first

slotA.appendChild(cardB);

slotB.appendChild(cardA);

const newRectA = cardA.getBoundingClientRect();

const newRectB = cardB.getBoundingClientRect();

// Animate from old positions

await Promise.all([

animate(cardA, {

x: [rectA.left - newRectA.left, 0],

y: [rectA.top - newRectA.top, 0],

scale: [1.1, 0.95, 1],

duration: 400, ease: 'outBack'

}),

animate(cardB, {

x: [rectB.left - newRectB.left, 0],

y: [rectB.top - newRectB.top, 0],

scale: [1, 1.05, 1],

duration: 400, ease: 'outBack'

})

]);

}

```

Slot Hover Feedback

```javascript

function animateSlotHover(slot, isOver) {

animate(slot, {

scale: isOver ? 1.03 : 1,

borderColor: isOver ? '#4CAF50' : '#999',

duration: 150, ease: 'out(2)'

});

}

```

---

Click Animations

```javascript

// Bounce feedback

animate(card, {

scale: [1, 1.15, 1],

rotate: [0, 5, -5, 0],

duration: 400, ease: 'outElastic(1, 0.5)'

});

// Disabled shake

animate(card, { x: [0, -8, 8, -6, 6, 0], duration: 300, ease: 'linear' });

```

---

Scroll-Driven Animation

```javascript

animate('.el', {

x: 300,

autoplay: onScroll({ target: '.el', enter: 'bottom', leave: 'top', sync: true })

});

```

---

SVG Animations

```javascript

animate(createDrawable('path'), { draw: '0 1' }); // Draw path

animate('.el', { ...createMotionPath('.path') }); // Motion path

animate('.shape1', { d: morphTo('.shape2') }); // Morph shapes

```

---

Text Splitting

```javascript

const { chars } = splitText('.text', { chars: true });

animate(chars, { y: [20, 0], opacity: [0, 1], delay: stagger(30) });

```

---

Built-in Draggable

```javascript

createDraggable('.el', {

x: true, y: true, snap: 50, container: '.bounds',

releaseEase: createSpring({ stiffness: 120, damping: 6 }),

onDrag: (d) => {}, onRelease: (d) => {}

});

```

---

V3 to V4 Migration

| V3 | V4 |

|----|-----|

| anime({ targets: '.el' }) | animate('.el', {...}) |

| easing: 'easeOutQuad' | ease: 'outQuad' |

| direction: 'alternate' | alternate: true |

| translateX: 100 | x: 100 |

| anime.stagger(100) | stagger(100) |

| .finished | .then() or await |