assets/README.md
# Anime.js - Assets
This directory contains starter templates and example documentation for Anime.js animations.
## Contents
### Starter Template (Recommended)
For a complete Anime.js starter template with Vite:
```bash
# Create new project with Vite (vanilla JS)
npm create vite@latest my-anime-app -- --template vanilla
# Navigate and install
cd my-anime-app
npm install
# Add Anime.js
npm install animejs
```
### Official Examples
The Anime.js team maintains excellent examples at:
- **Documentation**: https://animejs.com/documentation/
- **CodePen Examples**: https://codepen.io/collection/DxpqGJ/
- **Official Repository**: https://github.com/juliangarnier/anime
### Recommended Examples by Category
**Basic Animations:**
- Simple animation: https://codepen.io/juliangarnier/pen/KRwwOL
- Property parameters: https://codepen.io/juliangarnier/pen/LpWPgj
- Function-based values: https://codepen.io/juliangarnier/pen/gmOwJX
**Stagger Animations:**
- Basic stagger: https://codepen.io/juliangarnier/pen/PboRJN
- Stagger from center: https://codepen.io/juliangarnier/pen/WNNwQa
- Grid stagger: https://codepen.io/juliangarnier/pen/JXaKpP
- Stagger easing: https://codepen.io/juliangarnier/pen/QMMmQK
**Timeline Animations:**
- Basic timeline: https://codepen.io/juliangarnier/pen/grVWYq
- Timeline offsets: https://codepen.io/juliangarnier/pen/xLOXJX
- Timeline controls: https://codepen.io/juliangarnier/pen/oWjmWd
**SVG Animations:**
- Line drawing: https://codepen.io/juliangarnier/pen/XdqyNw
- Path morphing: https://codepen.io/juliangarnier/pen/mWEOmL
- Motion path: https://codepen.io/juliangarnier/pen/NqYKQL
**Advanced Patterns:**
- Keyframes: https://codepen.io/juliangarnier/pen/QMMmQK
- Scroll-driven: https://codepen.io/juliangarnier/pen/ZqyEJw
- Custom easing: https://codepen.io/juliangarnier/pen/YBBRvG
## Quick Start Template
Minimal Anime.js setup:
### package.json
```json
{
"name": "animejs-starter",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"animejs": "^3.2.2"
},
"devDependencies": {
"vite": "^5.0.8"
}
}
```
### index.html
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Anime.js Starter</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<div class="container">
<h1>Anime.js Animation Demo</h1>
<div class="example basic-animation">
<h2>Basic Animation</h2>
<div class="box box1"></div>
<button id="basic-btn">Play</button>
</div>
<div class="example stagger-animation">
<h2>Stagger Animation</h2>
<div class="stagger-grid">
<div class="stagger-item"></div>
<div class="stagger-item"></div>
<div class="stagger-item"></div>
<div class="stagger-item"></div>
<div class="stagger-item"></div>
<div class="stagger-item"></div>
</div>
<button id="stagger-btn">Play</button>
</div>
<div class="example timeline-animation">
<h2>Timeline Animation</h2>
<div class="timeline-elements">
<div class="timeline-box box1"></div>
<div class="timeline-box box2"></div>
<div class="timeline-box box3"></div>
</div>
<button id="timeline-btn">Play</button>
</div>
</div>
<script type="module" src="/main.js"></script>
</body>
</html>
```
### main.js
```javascript
import anime from 'animejs/lib/anime.es.js'
// Basic Animation
document.getElementById('basic-btn').addEventListener('click', () => {
anime({
targets: '.box1',
translateX: 250,
rotate: '1turn',
scale: [0.75, 1],
duration: 800,
easing: 'easeInOutQuad'
})
})
// Stagger Animation
document.getElementById('stagger-btn').addEventListener('click', () => {
anime({
targets: '.stagger-item',
scale: [0, 1],
delay: anime.stagger(100),
easing: 'easeOutElastic(1, .8)',
duration: 600
})
})
// Timeline Animation
document.getElementById('timeline-btn').addEventListener('click', () => {
const tl = anime.timeline({
easing: 'easeOutExpo',
duration: 750
})
tl.add({
targets: '.timeline-box.box1',
translateX: 250
})
.add({
targets: '.timeline-box.box2',
translateX: 250
}, '-=500')
.add({
targets: '.timeline-box.box3',
translateX: 250
}, '-=500')
})
```
### style.css
```css
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
min-height: 100vh;
padding: 2rem;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
h1 {
text-align: center;
margin-bottom: 3rem;
font-size: 3rem;
}
.example {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 12px;
padding: 2rem;
margin-bottom: 2rem;
}
h2 {
margin-bottom: 1.5rem;
font-size: 1.5rem;
}
.box {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
border-radius: 8px;
margin-bottom: 1rem;
}
.stagger-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
margin-bottom: 1rem;
}
.stagger-item {
width: 100%;
aspect-ratio: 1;
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
border-radius: 8px;
}
.timeline-elements {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
}
.timeline-box {
width: 60px;
height: 60px;
}
.timeline-box.box1 { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
.timeline-box.box2 { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
.timeline-box.box3 { background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); }
button {
background: white;
color: #764ba2;
border: none;
padding: 0.75rem 2rem;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s;
}
button:hover {
transform: scale(1.05);
}
button:active {
transform: scale(0.95);
}
```
## Common Patterns
### Pattern 1: Sequential List Reveal
```javascript
anime({
targets: '.list-item',
translateY: [30, 0],
opacity: [0, 1],
delay: anime.stagger(80),
easing: 'easeOutQuad',
duration: 600
})
```
### Pattern 2: Grid Expand from Center
```javascript
anime({
targets: '.grid-square',
scale: [0, 1],
delay: anime.stagger(30, {
grid: [10, 10],
from: 'center'
}),
easing: 'easeOutElastic(1, .8)',
duration: 600
})
```
### Pattern 3: SVG Line Drawing
```javascript
anime({
targets: 'path',
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutQuad',
duration: 2000,
delay: (el, i) => i * 250
})
```
### Pattern 4: Timeline Sequence
```javascript
const tl = anime.timeline({
easing: 'easeOutExpo',
duration: 750
})
tl.add({
targets: '.title',
translateY: [-50, 0],
opacity: [0, 1]
})
.add({
targets: '.subtitle',
translateY: [-30, 0],
opacity: [0, 1]
}, '-=500')
.add({
targets: '.button',
scale: [0, 1]
}, '-=300')
```
## Framework Integration
### With React
```jsx
import { useEffect, useRef } from 'react'
import anime from 'animejs/lib/anime.es.js'
function AnimatedComponent() {
const ref = useRef(null)
useEffect(() => {
const animation = anime({
targets: ref.current,
translateX: 250,
duration: 800,
easing: 'easeInOutQuad'
})
return () => animation.pause()
}, [])
return <div ref={ref}>Animated</div>
}
```
### With Vue
```javascript
export default {
mounted() {
anime({
targets: this.$el,
translateX: 250,
duration: 800
})
}
}
```
## TypeScript Support
For TypeScript projects, install types:
```bash
npm install --save-dev @types/animejs
```
## Performance Tips
1. **Use transforms and opacity** - GPU-accelerated properties
2. **Batch similar animations** - One animation for multiple targets
3. **Add will-change** to CSS for complex animations
4. **Use autoplay: false** for scroll-driven animations
5. **Pause animations** on cleanup in frameworks
6. **Limit element count** - Stagger works best with <100 elements
## Additional Resources
- **Official Documentation**: https://animejs.com/documentation/
- **API Reference**: https://animejs.com/documentation/#cssProperties
- **GitHub Repository**: https://github.com/juliangarnier/anime
- **CodePen Collection**: https://codepen.io/collection/DxpqGJ/
- **Community Examples**: https://animejs.com/documentation/#communityShowcase
---
For more patterns and reference documentation, see:
- `api_reference.md` - Complete Anime.js API
- `stagger_guide.md` - Stagger utilities deep dive
- `timeline_guide.md` - Timeline sequencing guide
references/api_reference.md
# Anime.js API Reference
Complete API documentation for Anime.js v3+.
## Table of Contents
- [anime() Function](#anime-function)
- [Targets](#targets)
- [Properties](#properties)
- [Property Parameters](#property-parameters)
- [Animation Parameters](#animation-parameters)
- [Timeline](#timeline)
- [Stagger](#stagger)
- [Easing Functions](#easing-functions)
- [Helpers](#helpers)
- [Callbacks](#callbacks)
---
## anime() Function
Main function to create animations.
```javascript
anime(options)
```
**Returns:** Animation instance with playback controls.
---
## Targets
Specify what to animate.
**CSS Selector:**
```javascript
targets: '.element'
targets: '#myId'
targets: 'div.className'
```
**DOM Element/NodeList:**
```javascript
targets: document.querySelector('.element')
targets: document.querySelectorAll('.elements')
```
**JavaScript Object:**
```javascript
const obj = { prop: 0 }
anime({ targets: obj, prop: 100 })
```
**Array:**
```javascript
targets: [el1, el2, el3]
targets: [obj1, obj2]
```
---
## Properties
### CSS Properties
**Transform (individual):**
```javascript
translateX: 250 // pixels
translateY: '10em' // units
translateZ: '50vh'
rotate: '1turn' // turns, deg, rad
rotateX, rotateY, rotateZ
scale: 2
scaleX, scaleY, scaleZ
skew: '10deg'
skewX, skewY
perspective: 1000
```
**Other CSS:**
```javascript
opacity: 0.5
backgroundColor: '#FFF' // colors
borderRadius: '50%'
width: '100px'
left: '50%'
// Any valid CSS property (camelCase)
```
### SVG Attributes
```javascript
d: 'M10 80 Q 77.5 10, 145 80' // Path morphing
points: '64 68 8 400 ...' // Polygon points
strokeDashoffset: 100
fill: '#FF0000'
r: 50 // Circle radius
cx, cy // Circle center
// Any SVG attribute
```
### DOM Attributes
```javascript
value: 100 // Input value
volume: 0.5 // Audio/video
textContent: 'Hello' // Text content
innerHTML: '<div></div>'
// Any DOM attribute
```
### JavaScript Object Properties
```javascript
const obj = { x: 0, y: 0 }
anime({
targets: obj,
x: 100,
y: 200
})
```
---
## Property Parameters
Define how each property animates.
### Value Formats
**Single Value:**
```javascript
translateX: 250 // Animate from current to 250
```
**From-To Array:**
```javascript
translateX: [0, 250] // Explicit from/to
opacity: [0, 1]
```
**Function-Based:**
```javascript
translateX: (el, i) => 50 + (i * 50) // Different per element
delay: (el, i, total) => i * 100
```
**Keyframes:**
```javascript
translateX: [
{ value: 100, duration: 500 },
{ value: 200, duration: 500 },
{ value: 0, duration: 500 }
]
```
### Units
```javascript
'250px' // Pixels
'10em' // Ems
'50%' // Percentage
'100vh' // Viewport
'1turn' // Turns
'45deg' // Degrees
'1.5rad' // Radians
```
### Relative Values
```javascript
translateX: '+=250' // Add 250 to current
translateX: '-=100' // Subtract 100
translateX: '*=2' // Multiply by 2
```
### Colors
```javascript
'#FF0000' // Hex
'rgb(255, 0, 0)' // RGB
'rgba(255, 0, 0, 0.5)' // RGBA
'hsl(0, 100%, 50%)' // HSL
```
---
## Animation Parameters
Global animation settings.
```javascript
anime({
targets: '.element',
translateX: 250,
// Timing
duration: 1000, // Milliseconds
delay: 500, // Start delay
endDelay: 200, // End delay
// Easing
easing: 'easeInOutQuad', // Built-in easing
// Playback
direction: 'normal', // 'normal', 'reverse', 'alternate'
loop: 3, // Number or true for infinite
autoplay: true, // Auto-start
// Advanced
round: 1, // Round values to 1 decimal
specific Property Parameters
specificProperty: {
value: 100,
duration: 2000,
delay: 500,
easing: 'linear'
}
})
```
---
## Timeline
Chain multiple animations with precise control.
### Create Timeline
```javascript
const tl = anime.timeline({
duration: 1000,
easing: 'easeOutExpo',
loop: true
})
```
### Add Animations
```javascript
tl.add({
targets: '.el1',
translateX: 250
})
.add({
targets: '.el2',
translateX: 250
}, '-=500') // Offset
```
### Timeline Parameters
All animation parameters plus:
```javascript
anime.timeline({
duration: 1000, // Default duration
delay: 500, // Default delay
easing: 'linear', // Default easing
direction: 'normal',
loop: false,
autoplay: true
})
```
### Offset
Control when animations start:
```javascript
'+=500' // Start 500ms after previous ends
'-=500' // Start 500ms before previous ends
500 // Start at absolute time 500ms
```
### Timeline Methods
```javascript
tl.play()
tl.pause()
tl.restart()
tl.reverse()
tl.seek(1000) // Seek to 1000ms
tl.add(animation) // Add animation
```
---
## Stagger
Distribute delays across multiple elements.
### Basic Stagger
```javascript
delay: anime.stagger(100) // Increment by 100ms
```
### Stagger Options
```javascript
delay: anime.stagger(100, {
start: 500, // Start delay
from: 'center', // 'first', 'last', 'center', index, [x, y]
direction: 'normal', // 'normal', 'reverse'
easing: 'easeOutQuad', // Easing for delay
grid: [10, 10], // Grid dimensions
axis: 'x' // 'x', 'y', null
})
```
### Stagger Values
```javascript
// Stagger delays
delay: anime.stagger(100)
// Stagger values
translateX: anime.stagger([0, 100, 200, 300])
// Stagger ranges
scale: anime.stagger([1, 2], { from: 'center' })
```
### Grid Stagger
```javascript
anime({
targets: '.grid-item',
scale: [0, 1],
delay: anime.stagger(50, {
grid: [14, 5],
from: 'center'
})
})
```
---
## Easing Functions
### Built-in Easings
**Linear:**
```javascript
'linear'
```
**Ease In/Out:**
```javascript
'easeInQuad', 'easeOutQuad', 'easeInOutQuad'
'easeInCubic', 'easeOutCubic', 'easeInOutCubic'
'easeInQuart', 'easeOutQuart', 'easeInOutQuart'
'easeInQuint', 'easeOutQuint', 'easeInOutQuint'
'easeInSine', 'easeOutSine', 'easeInOutSine'
'easeInExpo', 'easeOutExpo', 'easeInOutExpo'
'easeInCirc', 'easeOutCirc', 'easeInOutCirc'
'easeInBack', 'easeOutBack', 'easeInOutBack'
'easeInElastic', 'easeOutElastic', 'easeInOutElastic'
'easeInBounce', 'easeOutBounce', 'easeInOutBounce'
```
### Custom Easings
**Cubic Bezier:**
```javascript
easing: 'cubicBezier(.5, .05, .1, .3)'
```
**Spring Physics:**
```javascript
easing: 'spring(1, 80, 10, 0)'
// mass, stiffness, damping, velocity
```
**Steps:**
```javascript
easing: 'steps(5)' // 5 steps
```
**Linear Keyframes:**
```javascript
easing: 'linear(0, 0.5, 1)'
```
---
## Helpers
### anime.setDashoffset(el)
Calculate strokeDashoffset for line drawing:
```javascript
anime({
targets: 'path',
strokeDashoffset: [anime.setDashoffset, 0]
})
```
### anime.path(path)
Get coordinates and angle from SVG path:
```javascript
const path = anime.path('#motion-path')
anime({
targets: '.element',
translateX: path('x'),
translateY: path('y'),
rotate: path('angle')
})
```
### anime.random(min, max)
Generate random value:
```javascript
translateX: () => anime.random(0, 100)
```
### anime.get(targets, prop)
Get current value:
```javascript
const currentX = anime.get('.element', 'translateX')
```
### anime.set(targets, prop)
Set value without animating:
```javascript
anime.set('.element', { translateX: 100, opacity: 0.5 })
```
---
## Callbacks
### Animation Callbacks
```javascript
anime({
targets: '.element',
translateX: 250,
begin: (anim) => {
console.log('Animation begins')
},
update: (anim) => {
console.log('Animation updates', anim.progress)
},
complete: (anim) => {
console.log('Animation completes')
},
loopBegin: (anim) => {
console.log('Loop begins')
},
loopComplete: (anim) => {
console.log('Loop completes')
},
changeBegin: (anim) => {
console.log('Direction changes')
},
changeComplete: (anim) => {
console.log('Direction change completes')
}
})
```
### Callback Parameters
**anim Object:**
```javascript
anim.progress // Animation progress (0-100)
anim.currentTime // Current time (ms)
anim.duration // Total duration (ms)
anim.remaining // Remaining loops
anim.reversed // Direction reversed?
anim.paused // Animation paused?
anim.began // Animation began?
anim.finished // Animation finished?
```
---
## Animation Instance
### Properties
```javascript
const animation = anime({ ... })
animation.progress // 0-100
animation.currentTime // Milliseconds
animation.duration // Milliseconds
animation.remaining // Remaining loops
animation.reversed // Boolean
animation.paused // Boolean
animation.began // Boolean
animation.finished // Boolean
animation.animatables // Array of targets
animation.animations // Array of property animations
```
### Methods
```javascript
animation.play()
animation.pause()
animation.restart()
animation.reverse()
animation.seek(time) // Seek to time (ms or %)
animation.tick(time) // Manual tick
```
---
## Examples
### Basic Animation
```javascript
anime({
targets: '.box',
translateX: 250,
rotate: '1turn',
duration: 800,
easing: 'easeInOutQuad'
})
```
### From-To Animation
```javascript
anime({
targets: '.element',
translateX: [0, 250],
opacity: [0, 1],
duration: 1000
})
```
### Keyframe Animation
```javascript
anime({
targets: '.element',
keyframes: [
{ translateX: 100 },
{ translateY: 100 },
{ translateX: 0 },
{ translateY: 0 }
],
duration: 4000,
loop: true
})
```
### Timeline Animation
```javascript
const tl = anime.timeline()
tl.add({
targets: '.box1',
translateX: 250
})
.add({
targets: '.box2',
translateX: 250
}, '-=500')
```
### Stagger Animation
```javascript
anime({
targets: '.stagger-item',
translateY: [-50, 0],
opacity: [0, 1],
delay: anime.stagger(100, { from: 'center' })
})
```
### SVG Path Animation
```javascript
anime({
targets: '#morphing-path',
d: 'M10 80 Q 77.5 150, 145 80',
duration: 2000,
easing: 'easeInOutQuad'
})
```
### JavaScript Object Animation
```javascript
const obj = { count: 0 }
anime({
targets: obj,
count: 100,
round: 1,
update: () => {
document.querySelector('.counter').textContent = obj.count
}
})
```
references/stagger_guide.md
# Anime.js Stagger Guide
Complete guide to Anime.js stagger utilities for creating sequential and coordinated animations.
## Overview
Stagger distributes animation delays across multiple elements, creating cascading or wave-like effects. Anime.js provides powerful stagger options including grid-based staggering, directional control, and custom easing.
## Basic Stagger
### Time-Based Stagger
```javascript
anime({
targets: '.element',
translateX: 250,
delay: anime.stagger(100) // Increment delay by 100ms
})
```
**Result:** Each element animates 100ms after the previous one.
### Value-Based Stagger
```javascript
anime({
targets: '.element',
translateX: anime.stagger([0, 100, 200, 300])
})
```
**Result:** Each element animates to a different translateX value.
## Stagger Options
### Complete Syntax
```javascript
anime.stagger(value, {
start: 0, // Starting delay (ms)
from: 'first', // Starting point
direction: 'normal', // Direction of stagger
easing: 'linear', // Easing for stagger progression
grid: [rows, cols], // Grid dimensions
axis: null // Grid axis ('x', 'y', or null)
})
```
---
## Stagger From
Control where the stagger starts.
### from: 'first' (default)
Start from the first element:
```javascript
anime({
targets: '.element',
scale: [0, 1],
delay: anime.stagger(100, { from: 'first' })
})
```
### from: 'last'
Start from the last element:
```javascript
anime({
targets: '.element',
scale: [0, 1],
delay: anime.stagger(100, { from: 'last' })
})
```
### from: 'center'
Start from the center and expand outward:
```javascript
anime({
targets: '.element',
scale: [0, 1],
delay: anime.stagger(100, { from: 'center' })
})
```
### from: index
Start from a specific index:
```javascript
anime({
targets: '.element',
scale: [0, 1],
delay: anime.stagger(100, { from: 5 }) // Start from 6th element (0-indexed)
})
```
### from: [x, y]
Start from specific grid coordinates:
```javascript
anime({
targets: '.grid-item',
scale: [0, 1],
delay: anime.stagger(50, {
grid: [10, 10],
from: [5, 5] // Start from center of grid
})
})
```
---
## Grid Stagger
Stagger elements arranged in a grid pattern.
### Basic Grid
```javascript
anime({
targets: '.grid-item',
scale: [0, 1],
delay: anime.stagger(50, {
grid: [14, 5] // 14 columns, 5 rows
})
})
```
### Grid with from: 'center'
```javascript
anime({
targets: '.grid-item',
scale: [0, 1],
delay: anime.stagger(50, {
grid: [14, 5],
from: 'center'
})
})
```
**Result:** Animates from center outward in all directions.
### Grid with Axis
Control which axis dominates the stagger:
```javascript
// Horizontal waves
anime({
targets: '.grid-item',
translateY: [-20, 0],
delay: anime.stagger(30, {
grid: [10, 10],
from: 'center',
axis: 'x' // Stagger primarily along x-axis
})
})
// Vertical waves
anime({
targets: '.grid-item',
translateY: [-20, 0],
delay: anime.stagger(30, {
grid: [10, 10],
from: 'center',
axis: 'y' // Stagger primarily along y-axis
})
})
```
---
## Stagger Direction
### direction: 'normal' (default)
```javascript
anime({
targets: '.element',
scale: [0, 1],
delay: anime.stagger(100, { direction: 'normal' })
})
```
### direction: 'reverse'
Reverse the stagger order:
```javascript
anime({
targets: '.element',
scale: [0, 1],
delay: anime.stagger(100, {
from: 'first',
direction: 'reverse' // Last to first
})
})
```
---
## Stagger Easing
Apply easing to the stagger progression itself.
```javascript
anime({
targets: '.element',
translateY: [-50, 0],
delay: anime.stagger(100, {
easing: 'easeOutQuad' // Ease the delay distribution
})
})
```
**Effect:** Early elements have shorter delays, later elements have longer delays (exponential distribution).
---
## Stagger Start
Add an initial delay before stagger begins.
```javascript
anime({
targets: '.element',
scale: [0, 1],
delay: anime.stagger(100, {
start: 500 // Wait 500ms before starting stagger
})
})
```
**Result:**
- Element 1: 500ms delay
- Element 2: 600ms delay
- Element 3: 700ms delay
---
## Common Patterns
### 1. Sequential List Reveal
```javascript
anime({
targets: '.list-item',
translateY: [30, 0],
opacity: [0, 1],
delay: anime.stagger(80),
easing: 'easeOutQuad'
})
```
### 2. Grid Expand from Center
```javascript
anime({
targets: '.grid-square',
scale: [0, 1],
delay: anime.stagger(30, {
grid: [10, 10],
from: 'center'
}),
easing: 'easeOutElastic(1, .8)'
})
```
### 3. Wave Effect
```javascript
anime({
targets: '.wave-element',
translateY: [
{ value: -20, duration: 300 },
{ value: 0, duration: 300 }
],
delay: anime.stagger(50),
loop: true
})
```
### 4. Diagonal Wipe
```javascript
anime({
targets: '.tile',
opacity: [0, 1],
translateX: [-50, 0],
delay: anime.stagger(20, {
grid: [10, 10],
from: [0, 0], // Top-left corner
axis: null // Both axes
})
})
```
### 5. Text Character Reveal
```javascript
// Split text into characters first
const textWrapper = document.querySelector('.text')
textWrapper.innerHTML = textWrapper.textContent.replace(/\S/g, "<span class='letter'>$&</span>")
anime({
targets: '.letter',
translateY: [100, 0],
opacity: [0, 1],
delay: anime.stagger(30),
easing: 'easeOutExpo'
})
```
### 6. Circular Stagger
```javascript
anime({
targets: '.circle-item',
scale: [0, 1],
delay: anime.stagger(100, {
from: 'center',
easing: 'linear'
}),
rotate: anime.stagger([0, 360])
})
```
### 7. Alternating Direction
```javascript
anime({
targets: '.row',
translateX: (el, i) => i % 2 === 0 ? [-250, 0] : [250, 0],
opacity: [0, 1],
delay: anime.stagger(100)
})
```
---
## Stagger with Different Properties
### Stagger Delays, Same Values
```javascript
anime({
targets: '.element',
translateX: 250, // Same for all
delay: anime.stagger(100) // Different delays
})
```
### Stagger Values, Same Timing
```javascript
anime({
targets: '.element',
translateX: anime.stagger([0, 50, 100, 150]), // Different values
duration: 1000 // Same duration
})
```
### Stagger Both
```javascript
anime({
targets: '.element',
translateX: anime.stagger([0, 50, 100, 150]),
delay: anime.stagger(100)
})
```
---
## Advanced Techniques
### Dynamic Grid Calculation
```javascript
function getGridSize() {
const elements = document.querySelectorAll('.grid-item')
const cols = Math.ceil(Math.sqrt(elements.length))
const rows = Math.ceil(elements.length / cols)
return [cols, rows]
}
anime({
targets: '.grid-item',
scale: [0, 1],
delay: anime.stagger(50, {
grid: getGridSize(),
from: 'center'
})
})
```
### Stagger with Function Values
```javascript
anime({
targets: '.element',
translateX: anime.stagger((el, i, total) => {
// Custom calculation
return (i / total) * 100
}),
delay: anime.stagger(100)
})
```
### Multi-Property Stagger
```javascript
anime({
targets: '.element',
translateX: anime.stagger([0, 100]),
translateY: anime.stagger([0, 50]),
rotate: anime.stagger([0, 360]),
delay: anime.stagger(100, { from: 'center' })
})
```
---
## Performance Tips
1. **Use transforms** - Stagger `translateX`, `translateY`, `scale`, `rotate` for GPU acceleration
2. **Limit element count** - Stagger works best with <100 elements
3. **Avoid layout properties** - Don't stagger `width`, `height`, `left`, `top`
4. **Use `will-change`** - Add `will-change: transform` for smoother animations
5. **Batch similar animations** - One stagger for multiple properties is more efficient than separate animations
---
## Common Mistakes
### ❌ Wrong: Stagger on Single Element
```javascript
anime({
targets: '.single-element', // Only one element
scale: [0, 1],
delay: anime.stagger(100) // No effect
})
```
### ✅ Correct: Multiple Elements
```javascript
anime({
targets: '.multiple-elements', // Multiple elements
scale: [0, 1],
delay: anime.stagger(100)
})
```
### ❌ Wrong: Grid Without Dimensions
```javascript
anime({
targets: '.grid-item',
scale: [0, 1],
delay: anime.stagger(50, {
from: 'center' // Missing grid dimensions!
})
})
```
### ✅ Correct: Grid With Dimensions
```javascript
anime({
targets: '.grid-item',
scale: [0, 1],
delay: anime.stagger(50, {
grid: [10, 10],
from: 'center'
})
})
```
---
## Debugging Tips
**Log stagger values:**
```javascript
anime({
targets: '.element',
translateX: 250,
delay: anime.stagger(100),
begin: (anim) => {
anim.animatables.forEach((animatable, i) => {
console.log(`Element ${i}: delay ${animatable.delay}ms`)
})
}
})
```
**Visualize stagger:**
```javascript
// Add data-index to elements
document.querySelectorAll('.element').forEach((el, i) => {
el.setAttribute('data-index', i)
})
```
---
## Resources
- Official Stagger Documentation: https://animejs.com/documentation/#stagger
- Grid Stagger Demo: https://codepen.io/juliangarnier/pen/JXaKpP
- Stagger Examples: https://animejs.com/documentation/#staggeringBasics
references/timeline_guide.md
# Anime.js Timeline Guide
Complete guide to creating complex animation sequences with Anime.js timelines.
## Overview
Timelines allow you to chain multiple animations with precise control over timing and sequencing. They provide a declarative way to choreograph complex multi-step animations.
## Creating a Timeline
### Basic Timeline
```javascript
const timeline = anime.timeline()
```
### Timeline with Default Parameters
```javascript
const timeline = anime.timeline({
duration: 1000, // Default duration for all animations
easing: 'easeOutExpo', // Default easing
loop: false, // Loop entire timeline
direction: 'normal', // 'normal', 'reverse', 'alternate'
autoplay: true // Auto-start
})
```
---
## Adding Animations
### .add() Method
```javascript
timeline.add({
targets: '.element',
translateX: 250,
duration: 800
})
```
### Chaining Animations
```javascript
timeline
.add({
targets: '.box1',
translateX: 250
})
.add({
targets: '.box2',
translateX: 250
})
.add({
targets: '.box3',
translateX: 250
})
```
---
## Timeline Offsets
Control when each animation starts relative to the previous one.
### Absolute Time
```javascript
timeline.add({
targets: '.element',
translateX: 250
}, 1000) // Start at 1000ms from timeline start
```
### Relative to Previous (+=)
```javascript
timeline
.add({
targets: '.box1',
translateX: 250,
duration: 1000
})
.add({
targets: '.box2',
translateX: 250
}, '+=500') // Start 500ms AFTER box1 completes
```
### Overlap with Previous (-=)
```javascript
timeline
.add({
targets: '.box1',
translateX: 250,
duration: 1000
})
.add({
targets: '.box2',
translateX: 250
}, '-=500') // Start 500ms BEFORE box1 completes (overlap)
```
---
## Common Patterns
### 1. Sequential Animations
Each animation starts after the previous completes:
```javascript
const tl = anime.timeline()
tl.add({
targets: '.title',
translateY: [-50, 0],
opacity: [0, 1]
})
.add({
targets: '.subtitle',
translateY: [-30, 0],
opacity: [0, 1]
})
.add({
targets: '.button',
scale: [0, 1]
})
```
### 2. Overlapping Animations
Create smooth transitions between animations:
```javascript
const tl = anime.timeline()
tl.add({
targets: '.section1',
opacity: [1, 0],
duration: 600
})
.add({
targets: '.section2',
opacity: [0, 1],
duration: 600
}, '-=300') // Overlap by 300ms for crossfade
```
### 3. Staggered Timeline
Combine timeline with stagger:
```javascript
const tl = anime.timeline()
tl.add({
targets: '.card',
translateY: [100, 0],
opacity: [0, 1],
delay: anime.stagger(100)
})
.add({
targets: '.button',
scale: [0, 1]
}, '-=200')
```
### 4. Multi-Stage Animation
Complex multi-step sequence:
```javascript
const tl = anime.timeline({
easing: 'easeOutExpo',
duration: 750
})
tl.add({
targets: '.modal',
scale: [0, 1],
opacity: [0, 1]
})
.add({
targets: '.modal-header',
translateY: [-20, 0],
opacity: [0, 1]
}, '-=500')
.add({
targets: '.modal-body',
translateY: [20, 0],
opacity: [0, 1]
}, '-=400')
.add({
targets: '.modal-footer',
opacity: [0, 1]
}, '-=300')
```
### 5. Looping Timeline
```javascript
const tl = anime.timeline({
loop: true,
direction: 'alternate'
})
tl.add({
targets: '.ball',
translateY: -200,
duration: 1000
})
.add({
targets: '.ball',
translateX: 200,
duration: 1000
})
```
### 6. Loading Sequence
```javascript
const tl = anime.timeline()
tl.add({
targets: '.loader-bg',
scaleY: [0, 1],
duration: 400,
easing: 'easeInOutQuad'
})
.add({
targets: '.loader-text',
opacity: [0, 1],
translateY: [20, 0],
duration: 600
}, '-=200')
.add({
targets: '.loader-spinner',
rotate: '1turn',
duration: 800,
loop: 3
}, '-=400')
.add({
targets: '.loader',
opacity: 0,
duration: 400
}, '+=500')
.add({
targets: '.content',
translateY: [50, 0],
opacity: [0, 1],
duration: 600
})
```
### 7. Page Transition
```javascript
function pageTransition(oldPage, newPage) {
const tl = anime.timeline()
tl.add({
targets: oldPage,
translateX: [0, -100],
opacity: [1, 0],
duration: 400,
easing: 'easeInQuad'
})
.add({
targets: newPage,
translateX: [100, 0],
opacity: [0, 1],
duration: 400,
easing: 'easeOutQuad'
}, '-=200')
return tl
}
```
---
## Timeline Controls
### Playback Methods
```javascript
const tl = anime.timeline({ autoplay: false })
tl.play() // Play from current position
tl.pause() // Pause
tl.restart() // Restart from beginning
tl.reverse() // Reverse direction
tl.seek(2000) // Seek to 2000ms
```
### Properties
```javascript
tl.duration // Total duration
tl.currentTime // Current time
tl.progress // Progress (0-100)
tl.reversed // Is reversed?
tl.paused // Is paused?
tl.began // Has begun?
tl.finished // Is finished?
```
---
## Advanced Techniques
### Labels
Mark specific points in the timeline:
```javascript
const tl = anime.timeline()
tl.add({
targets: '.intro',
opacity: [0, 1]
})
.add({}, '+=500') // Add label "intro-done" at this point
.add({
targets: '.content',
translateY: [50, 0],
opacity: [0, 1]
})
// Seek to label
tl.seek('intro-done')
```
### Nested Timelines
```javascript
function createIntroTimeline() {
const tl = anime.timeline({ autoplay: false })
tl.add({
targets: '.logo',
scale: [0, 1]
})
.add({
targets: '.tagline',
opacity: [0, 1]
})
return tl
}
const mainTimeline = anime.timeline()
mainTimeline.add(createIntroTimeline())
```
### Timeline Callbacks
```javascript
const tl = anime.timeline({
begin: () => console.log('Timeline begins'),
update: (tl) => console.log('Progress:', tl.progress),
complete: () => console.log('Timeline completes')
})
```
### Conditional Timeline
```javascript
const tl = anime.timeline()
tl.add({
targets: '.element1',
translateX: 250
})
if (condition) {
tl.add({
targets: '.element2',
opacity: [0, 1]
})
}
tl.add({
targets: '.element3',
scale: [0, 1]
})
```
---
## Timeline vs Individual Animations
### Without Timeline (Hard to Manage)
```javascript
anime({
targets: '.box1',
translateX: 250,
duration: 1000
})
setTimeout(() => {
anime({
targets: '.box2',
translateX: 250,
duration: 1000
})
}, 1000)
setTimeout(() => {
anime({
targets: '.box3',
translateX: 250,
duration: 1000
})
}, 2000)
```
### With Timeline (Clean & Maintainable)
```javascript
const tl = anime.timeline()
tl.add({
targets: '.box1',
translateX: 250,
duration: 1000
})
.add({
targets: '.box2',
translateX: 250,
duration: 1000
})
.add({
targets: '.box3',
translateX: 250,
duration: 1000
})
```
---
## Performance Optimization
### 1. Use Relative Offsets
```javascript
// ✅ Good: Flexible timing
.add({...}, '-=500')
// ❌ Avoid: Brittle absolute timing
.add({...}, 1500)
```
### 2. Batch Similar Animations
```javascript
// ✅ Good: Single animation
tl.add({
targets: ['.el1', '.el2', '.el3'],
translateX: 250
})
// ❌ Avoid: Multiple separate animations
tl.add({ targets: '.el1', translateX: 250 })
.add({ targets: '.el2', translateX: 250 })
.add({ targets: '.el3', translateX: 250 })
```
### 3. Set Default Parameters
```javascript
// ✅ Good: DRY
const tl = anime.timeline({
easing: 'easeOutExpo',
duration: 800
})
// ❌ Avoid: Repetition
tl.add({ targets: '.el1', easing: 'easeOutExpo', duration: 800 })
.add({ targets: '.el2', easing: 'easeOutExpo', duration: 800 })
```
---
## Common Mistakes
### ❌ Wrong: Missing Offset Operator
```javascript
tl.add({
targets: '.box1',
translateX: 250
})
.add({
targets: '.box2',
translateX: 250
}, '500') // Treated as absolute time!
```
### ✅ Correct: Use Relative Operator
```javascript
tl.add({
targets: '.box1',
translateX: 250
})
.add({
targets: '.box2',
translateX: 250
}, '+=500') // Relative to previous
```
### ❌ Wrong: Forgetting autoplay: false
```javascript
const tl = anime.timeline() // Starts immediately
// Later...
button.addEventListener('click', () => {
tl.play() // Already playing!
})
```
### ✅ Correct: Disable autoplay
```javascript
const tl = anime.timeline({ autoplay: false })
button.addEventListener('click', () => {
tl.play() // Now it works
})
```
---
## Real-World Examples
### Hero Section Animation
```javascript
const heroTimeline = anime.timeline({
easing: 'easeOutExpo'
})
heroTimeline
.add({
targets: '.hero-bg',
scale: [1.2, 1],
opacity: [0, 1],
duration: 1200
})
.add({
targets: '.hero-title',
translateY: [100, 0],
opacity: [0, 1],
duration: 800
}, '-=800')
.add({
targets: '.hero-subtitle',
translateY: [50, 0],
opacity: [0, 1],
duration: 600
}, '-=400')
.add({
targets: '.hero-cta',
scale: [0, 1],
duration: 400
}, '-=200')
```
### Card Flip Animation
```javascript
function flipCard(card) {
const tl = anime.timeline({ autoplay: false })
tl.add({
targets: card.querySelector('.front'),
rotateY: [0, 90],
duration: 300,
easing: 'easeInQuad'
})
.add({
targets: card.querySelector('.back'),
rotateY: [-90, 0],
duration: 300,
easing: 'easeOutQuad'
}, '-=0')
return tl
}
```
### Notification Toast
```javascript
function showToast(toast) {
const tl = anime.timeline()
tl.add({
targets: toast,
translateX: [400, 0],
opacity: [0, 1],
duration: 400,
easing: 'easeOutBack'
})
.add({
targets: toast,
opacity: [1, 0],
duration: 300,
easing: 'easeInQuad'
}, '+=3000')
return tl
}
```
---
## Debugging Timelines
### Log Timeline Progress
```javascript
const tl = anime.timeline({
update: (tl) => {
console.log(`Progress: ${tl.progress.toFixed(2)}%`)
console.log(`Current time: ${tl.currentTime}ms`)
}
})
```
### Visualize Timeline
```javascript
const tl = anime.timeline()
tl.add({
targets: '.box1',
translateX: 250,
duration: 1000,
begin: () => console.log('[0ms] Box1 begins'),
complete: () => console.log('[1000ms] Box1 completes')
})
.add({
targets: '.box2',
translateX: 250,
duration: 1000,
begin: () => console.log('[1000ms] Box2 begins'),
complete: () => console.log('[2000ms] Box2 completes')
}, '-=500')
```
---
## Resources
- Official Timeline Documentation: https://animejs.com/documentation/#timeline
- Timeline Examples: https://codepen.io/collection/DxpqGJ/
- Advanced Sequencing: https://animejs.com/documentation/#timelineBasics
scripts/animation_generator.py
#!/usr/bin/env python3
"""
Anime.js Animation Generator
Generates Anime.js animation boilerplate code for common animation patterns.
Usage:
./animation_generator.py # Interactive mode
./animation_generator.py --type stagger # Generate stagger animation
./animation_generator.py --type timeline # Generate timeline sequence
Animation Types:
basic - Simple translateX/Y, opacity animation
stagger - Sequential reveal with stagger
grid-stagger - Grid-based stagger animation
svg-line - SVG line drawing animation
svg-morph - SVG path morphing animation
timeline - Multi-step timeline sequence
keyframe - Keyframe animation with multiple steps
scroll - Scroll-triggered animation
"""
import sys
ANIMATION_TYPES = {
'basic': {
'name': 'Basic Animation',
'description': 'Simple translateX/Y, opacity animation',
'code': '''anime({
targets: '.element',
translateX: 250,
rotate: '1turn',
opacity: [0, 1],
duration: 800,
easing: 'easeInOutQuad'
})'''
},
'stagger': {
'name': 'Stagger Animation',
'description': 'Sequential reveal with stagger effect',
'code': '''anime({
targets: '.stagger-element',
translateY: [100, 0],
opacity: [0, 1],
delay: anime.stagger(100), // Increment delay by 100ms
easing: 'easeOutQuad',
duration: 600
})'''
},
'grid-stagger': {
'name': 'Grid Stagger Animation',
'description': 'Grid-based stagger from center',
'code': '''anime({
targets: '.grid-item',
scale: [0, 1],
delay: anime.stagger(50, {
grid: [14, 5], // 14 columns, 5 rows
from: 'center', // Start from center
axis: 'x' // Primary axis
}),
easing: 'easeOutElastic(1, .8)',
duration: 600
})'''
},
'svg-line': {
'name': 'SVG Line Drawing',
'description': 'SVG path line drawing animation',
'code': '''anime({
targets: 'path',
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutQuad',
duration: 2000,
delay: (el, i) => i * 250
})'''
},
'svg-morph': {
'name': 'SVG Path Morphing',
'description': 'Morph between SVG path shapes',
'code': '''anime({
targets: '#morphing-path',
d: [
{ value: 'M10 80 Q 77.5 10, 145 80' }, // Start shape
{ value: 'M10 80 Q 77.5 150, 145 80' } // End shape
],
duration: 2000,
easing: 'easeInOutQuad',
loop: true,
direction: 'alternate'
})'''
},
'timeline': {
'name': 'Timeline Sequence',
'description': 'Multi-step timeline with overlapping animations',
'code': '''const tl = anime.timeline({
easing: 'easeOutExpo',
duration: 750
})
tl.add({
targets: '.title',
translateY: [-50, 0],
opacity: [0, 1]
})
.add({
targets: '.subtitle',
translateY: [-30, 0],
opacity: [0, 1]
}, '-=500') // Start 500ms before previous ends
.add({
targets: '.button',
scale: [0, 1],
opacity: [0, 1]
}, '-=300')'''
},
'keyframe': {
'name': 'Keyframe Animation',
'description': 'Multiple keyframes for complex motion',
'code': '''anime({
targets: '.element',
keyframes: [
{ translateX: 100 },
{ translateY: 100 },
{ translateX: 0 },
{ translateY: 0 }
],
duration: 4000,
easing: 'easeInOutQuad',
loop: true
})'''
},
'scroll': {
'name': 'Scroll-Triggered Animation',
'description': 'Animation controlled by scroll position',
'code': '''const animation = anime({
targets: '.scroll-element',
translateY: [100, 0],
opacity: [0, 1],
easing: 'easeOutQuad',
autoplay: false
})
window.addEventListener('scroll', () => {
const scrollPercent = window.scrollY / (document.body.scrollHeight - window.innerHeight)
animation.seek(animation.duration * scrollPercent)
})'''
}
}
def print_header():
"""Print script header"""
print("\n" + "="*60)
print("Anime.js Animation Generator")
print("="*60 + "\n")
def list_animation_types():
"""List all available animation types"""
print("Available Animation Types:\n")
for key, anim in ANIMATION_TYPES.items():
print(f" {key:15} - {anim['description']}")
print()
def generate_animation(anim_type):
"""Generate animation code for given type"""
if anim_type not in ANIMATION_TYPES:
print(f"Error: Unknown animation type '{anim_type}'")
print("Use --list to see available types")
sys.exit(1)
anim = ANIMATION_TYPES[anim_type]
print(f"\n{anim['name']}")
print("-" * 60)
print(f"\n{anim['description']}\n")
print("Generated Code:")
print("-" * 60)
print(anim['code'])
print("\n" + "="*60 + "\n")
def interactive_mode():
"""Run in interactive mode"""
print_header()
list_animation_types()
print("Enter animation type (or 'quit' to exit): ", end='')
choice = input().strip().lower()
if choice == 'quit':
sys.exit(0)
generate_animation(choice)
def main():
"""Main entry point"""
args = sys.argv[1:]
# No arguments - interactive mode
if not args:
interactive_mode()
return
# Parse arguments
if '--list' in args:
print_header()
list_animation_types()
return
if '--type' in args:
try:
type_index = args.index('--type')
anim_type = args[type_index + 1]
print_header()
generate_animation(anim_type)
except IndexError:
print("Error: --type requires an animation type")
print("Usage: ./animation_generator.py --type <type>")
sys.exit(1)
return
# Help or unknown arguments
print(__doc__)
if __name__ == '__main__':
main()
scripts/timeline_builder.py
#!/usr/bin/env python3
"""
Anime.js Timeline Builder
Build complex Anime.js timeline sequences with interactive configuration.
Usage:
./timeline_builder.py # Interactive mode
./timeline_builder.py --preset hero # Use preset configuration
./timeline_builder.py --preset modal # Modal animation sequence
Timeline Presets:
hero - Hero section entrance animation
modal - Modal popup with content reveal
cards - Card stagger with button reveal
loader - Loading sequence with spinner
page - Page transition animation
toast - Notification toast animation
menu - Menu open/close animation
"""
import sys
TIMELINE_PRESETS = {
'hero': {
'name': 'Hero Section Animation',
'description': 'Hero background, title, subtitle, and CTA reveal',
'code': '''const heroTimeline = anime.timeline({
easing: 'easeOutExpo'
})
heroTimeline
.add({
targets: '.hero-bg',
scale: [1.2, 1],
opacity: [0, 1],
duration: 1200
})
.add({
targets: '.hero-title',
translateY: [100, 0],
opacity: [0, 1],
duration: 800
}, '-=800') // Overlap by 800ms
.add({
targets: '.hero-subtitle',
translateY: [50, 0],
opacity: [0, 1],
duration: 600
}, '-=400')
.add({
targets: '.hero-cta',
scale: [0, 1],
duration: 400
}, '-=200')'''
},
'modal': {
'name': 'Modal Popup Animation',
'description': 'Modal entrance with header, body, and footer reveal',
'code': '''const tl = anime.timeline({
easing: 'easeOutExpo',
duration: 750
})
tl.add({
targets: '.modal',
scale: [0, 1],
opacity: [0, 1]
})
.add({
targets: '.modal-header',
translateY: [-20, 0],
opacity: [0, 1]
}, '-=500')
.add({
targets: '.modal-body',
translateY: [20, 0],
opacity: [0, 1]
}, '-=400')
.add({
targets: '.modal-footer',
opacity: [0, 1]
}, '-=300')'''
},
'cards': {
'name': 'Card Grid Animation',
'description': 'Cards stagger in, then button appears',
'code': '''const tl = anime.timeline()
tl.add({
targets: '.card',
translateY: [100, 0],
opacity: [0, 1],
delay: anime.stagger(100),
duration: 600,
easing: 'easeOutQuad'
})
.add({
targets: '.button',
scale: [0, 1],
duration: 400,
easing: 'easeOutBack'
}, '-=200')'''
},
'loader': {
'name': 'Loading Sequence',
'description': 'Loader background, text, spinner, then fade to content',
'code': '''const tl = anime.timeline()
tl.add({
targets: '.loader-bg',
scaleY: [0, 1],
duration: 400,
easing: 'easeInOutQuad'
})
.add({
targets: '.loader-text',
opacity: [0, 1],
translateY: [20, 0],
duration: 600
}, '-=200')
.add({
targets: '.loader-spinner',
rotate: '1turn',
duration: 800,
loop: 3
}, '-=400')
.add({
targets: '.loader',
opacity: 0,
duration: 400
}, '+=500')
.add({
targets: '.content',
translateY: [50, 0],
opacity: [0, 1],
duration: 600
})'''
},
'page': {
'name': 'Page Transition',
'description': 'Slide out old page, slide in new page',
'code': '''function pageTransition(oldPage, newPage) {
const tl = anime.timeline()
tl.add({
targets: oldPage,
translateX: [0, -100],
opacity: [1, 0],
duration: 400,
easing: 'easeInQuad'
})
.add({
targets: newPage,
translateX: [100, 0],
opacity: [0, 1],
duration: 400,
easing: 'easeOutQuad'
}, '-=200') // Overlap by 200ms for smooth transition
return tl
}'''
},
'toast': {
'name': 'Toast Notification',
'description': 'Toast slides in, stays, then fades out',
'code': '''function showToast(toast) {
const tl = anime.timeline()
tl.add({
targets: toast,
translateX: [400, 0],
opacity: [0, 1],
duration: 400,
easing: 'easeOutBack'
})
.add({
targets: toast,
opacity: [1, 0],
duration: 300,
easing: 'easeInQuad'
}, '+=3000') // Wait 3 seconds before fading out
return tl
}'''
},
'menu': {
'name': 'Menu Open Animation',
'description': 'Menu slides in with staggered items',
'code': '''const tl = anime.timeline({ autoplay: false })
tl.add({
targets: '.menu-overlay',
translateX: ['-100%', 0],
duration: 400,
easing: 'easeOutQuad'
})
.add({
targets: '.menu-item',
translateX: [-50, 0],
opacity: [0, 1],
delay: anime.stagger(80),
duration: 500,
easing: 'easeOutExpo'
}, '-=200')
// Play on button click
document.querySelector('.menu-button').addEventListener('click', () => {
tl.play()
})'''
}
}
def print_header():
"""Print script header"""
print("\n" + "="*60)
print("Anime.js Timeline Builder")
print("="*60 + "\n")
def list_presets():
"""List all available timeline presets"""
print("Available Timeline Presets:\n")
for key, preset in TIMELINE_PRESETS.items():
print(f" {key:12} - {preset['description']}")
print()
def generate_timeline(preset_name):
"""Generate timeline code for given preset"""
if preset_name not in TIMELINE_PRESETS:
print(f"Error: Unknown preset '{preset_name}'")
print("Use --list to see available presets")
sys.exit(1)
preset = TIMELINE_PRESETS[preset_name]
print(f"\n{preset['name']}")
print("-" * 60)
print(f"\n{preset['description']}\n")
print("Generated Timeline:")
print("-" * 60)
print(preset['code'])
print("\n" + "="*60 + "\n")
def generate_custom_timeline():
"""Generate custom timeline with user input"""
print("\nCustom Timeline Builder")
print("-" * 60)
print("\nEnter number of animation steps: ", end='')
try:
num_steps = int(input().strip())
except ValueError:
print("Error: Invalid number")
sys.exit(1)
if num_steps < 1 or num_steps > 10:
print("Error: Number of steps must be between 1 and 10")
sys.exit(1)
print("\nGenerating timeline with", num_steps, "steps...\n")
# Generate timeline code
code = "const tl = anime.timeline({\n"
code += " duration: 750,\n"
code += " easing: 'easeOutExpo'\n"
code += "})\n\n"
for i in range(num_steps):
code += f"tl.add({{\n"
code += f" targets: '.element-{i+1}',\n"
code += f" translateY: [50, 0],\n"
code += f" opacity: [0, 1]\n"
if i > 0:
code += f"}}, '-=300') // Step {i+1}\n"
else:
code += f"}}) // Step {i+1}\n"
if i < num_steps - 1:
code += "\n"
print("Generated Timeline:")
print("-" * 60)
print(code)
print("\n" + "="*60 + "\n")
def interactive_mode():
"""Run in interactive mode"""
print_header()
list_presets()
print("Enter preset name (or 'custom' for custom builder, 'quit' to exit): ", end='')
choice = input().strip().lower()
if choice == 'quit':
sys.exit(0)
elif choice == 'custom':
generate_custom_timeline()
else:
generate_timeline(choice)
def main():
"""Main entry point"""
args = sys.argv[1:]
# No arguments - interactive mode
if not args:
interactive_mode()
return
# Parse arguments
if '--list' in args:
print_header()
list_presets()
return
if '--preset' in args:
try:
preset_index = args.index('--preset')
preset_name = args[preset_index + 1]
print_header()
generate_timeline(preset_name)
except IndexError:
print("Error: --preset requires a preset name")
print("Usage: ./timeline_builder.py --preset <name>")
sys.exit(1)
return
# Help or unknown arguments
print(__doc__)
if __name__ == '__main__':
main()
SKILL.md
---
name: animejs
description: Versatile JavaScript animation engine for DOM, CSS, SVG, and JavaScript objects. Use when creating timeline-based animations, stagger effects, SVG morphing, keyframe sequences, or complex choreographed animations. Triggers on tasks involving Anime.js, timeline animations, staggered sequences, SVG path animations, morphing, or multi-step animation choreography. Alternative to GSAP for SVG-heavy animations and React-independent projects.
---
# Anime.js
Lightweight JavaScript animation library with powerful timeline and stagger capabilities for web animations.
## Overview
Anime.js (pronounced "Anime JS") is a versatile animation engine that works with DOM elements, CSS properties, SVG attributes, and JavaScript objects. Unlike React-specific libraries, Anime.js works with vanilla JavaScript and any framework.
**When to use this skill:**
- Timeline-based animation sequences with precise choreography
- Staggered animations across multiple elements
- SVG path morphing and drawing animations
- Keyframe animations with percentage-based timing
- Framework-agnostic animation (works with React, Vue, vanilla JS)
- Complex easing functions (spring, steps, cubic-bezier)
**Core features:**
- Timeline sequencing with relative positioning
- Powerful stagger utilities (grid, from center, easing)
- SVG morphing and path animations
- Built-in spring physics easing
- Keyframe support with flexible timing
- Small bundle size (~9KB gzipped)
## Core Concepts
### Basic Animation
The `anime()` function creates animations:
```javascript
import anime from 'animejs'
anime({
targets: '.element',
translateX: 250,
rotate: '1turn',
duration: 800,
easing: 'easeInOutQuad'
})
```
### Targets
Multiple ways to specify animation targets:
```javascript
// CSS selector
anime({ targets: '.box' })
// DOM elements
anime({ targets: document.querySelectorAll('.box') })
// Array of elements
anime({ targets: [el1, el2, el3] })
// JavaScript object
const obj = { x: 0 }
anime({ targets: obj, x: 100 })
```
### Animatable Properties
**CSS Properties:**
```javascript
anime({
targets: '.element',
translateX: 250,
scale: 2,
opacity: 0.5,
backgroundColor: '#FFF'
})
```
**CSS Transforms (Individual):**
```javascript
anime({
targets: '.element',
translateX: 250, // Individual transform
rotate: '1turn', // Not 'transform: rotate()'
scale: 2
})
```
**SVG Attributes:**
```javascript
anime({
targets: 'path',
d: 'M10 80 Q 77.5 10, 145 80', // Path morphing
fill: '#FF0000',
strokeDashoffset: [anime.setDashoffset, 0] // Line drawing
})
```
**JavaScript Objects:**
```javascript
const obj = { value: 0 }
anime({
targets: obj,
value: 100,
round: 1,
update: () => console.log(obj.value)
})
```
### Timeline
Create complex sequences with precise control:
```javascript
const timeline = anime.timeline({
duration: 750,
easing: 'easeOutExpo'
})
timeline
.add({
targets: '.box1',
translateX: 250
})
.add({
targets: '.box2',
translateX: 250
}, '-=500') // Start 500ms before previous animation ends
.add({
targets: '.box3',
translateX: 250
}, '+=200') // Start 200ms after previous animation ends
```
## Common Patterns
### 1. Stagger Animation (Sequential Reveal)
```javascript
anime({
targets: '.stagger-element',
translateY: [100, 0],
opacity: [0, 1],
delay: anime.stagger(100), // Increase delay by 100ms
easing: 'easeOutQuad',
duration: 600
})
```
### 2. Stagger from Center
```javascript
anime({
targets: '.grid-item',
scale: [0, 1],
delay: anime.stagger(50, {
grid: [14, 5],
from: 'center', // Also: 'first', 'last', index, [x, y]
axis: 'x' // Also: 'y', null
}),
easing: 'easeOutQuad'
})
```
### 3. SVG Line Drawing
```javascript
anime({
targets: 'path',
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutQuad',
duration: 2000,
delay: (el, i) => i * 250
})
```
### 4. SVG Morphing
```javascript
anime({
targets: '#morphing-path',
d: [
{ value: 'M10 80 Q 77.5 10, 145 80' }, // Start shape
{ value: 'M10 80 Q 77.5 150, 145 80' } // End shape
],
duration: 2000,
easing: 'easeInOutQuad',
loop: true,
direction: 'alternate'
})
```
### 5. Timeline Sequence
```javascript
const tl = anime.timeline({
easing: 'easeOutExpo',
duration: 750
})
tl.add({
targets: '.title',
translateY: [-50, 0],
opacity: [0, 1]
})
.add({
targets: '.subtitle',
translateY: [-30, 0],
opacity: [0, 1]
}, '-=500')
.add({
targets: '.button',
scale: [0, 1],
opacity: [0, 1]
}, '-=300')
```
### 6. Keyframe Animation
```javascript
anime({
targets: '.element',
keyframes: [
{ translateX: 100 },
{ translateY: 100 },
{ translateX: 0 },
{ translateY: 0 }
],
duration: 4000,
easing: 'easeInOutQuad',
loop: true
})
```
### 7. Scroll-Triggered Animation
```javascript
const animation = anime({
targets: '.scroll-element',
translateY: [100, 0],
opacity: [0, 1],
easing: 'easeOutQuad',
autoplay: false
})
window.addEventListener('scroll', () => {
const scrollPercent = window.scrollY / (document.body.scrollHeight - window.innerHeight)
animation.seek(animation.duration * scrollPercent)
})
```
## Integration Patterns
### With React
```javascript
import { useEffect, useRef } from 'react'
import anime from 'animejs'
function AnimatedComponent() {
const ref = useRef(null)
useEffect(() => {
const animation = anime({
targets: ref.current,
translateX: 250,
duration: 800,
easing: 'easeInOutQuad'
})
return () => animation.pause()
}, [])
return <div ref={ref}>Animated</div>
}
```
### With Vue
```javascript
export default {
mounted() {
anime({
targets: this.$el,
translateX: 250,
duration: 800
})
}
}
```
### Path Following Animation
```javascript
const path = anime.path('#motion-path')
anime({
targets: '.element',
translateX: path('x'),
translateY: path('y'),
rotate: path('angle'),
easing: 'linear',
duration: 2000,
loop: true
})
```
## Advanced Techniques
### Spring Easing
```javascript
anime({
targets: '.element',
translateX: 250,
easing: 'spring(1, 80, 10, 0)', // mass, stiffness, damping, velocity
duration: 2000
})
```
### Steps Easing
```javascript
anime({
targets: '.element',
translateX: 250,
easing: 'steps(5)',
duration: 1000
})
```
### Custom Bezier
```javascript
anime({
targets: '.element',
translateX: 250,
easing: 'cubicBezier(.5, .05, .1, .3)',
duration: 1000
})
```
### Direction and Loop
```javascript
anime({
targets: '.element',
translateX: 250,
direction: 'alternate', // 'normal', 'reverse', 'alternate'
loop: true, // or number of iterations
easing: 'easeInOutQuad'
})
```
### Playback Control
```javascript
const animation = anime({
targets: '.element',
translateX: 250,
autoplay: false
})
animation.play()
animation.pause()
animation.restart()
animation.reverse()
animation.seek(500) // Seek to 500ms
```
## Performance Optimization
### Use Transform and Opacity
```javascript
// ✅ Good: GPU-accelerated
anime({
targets: '.element',
translateX: 250,
opacity: 0.5
})
// ❌ Avoid: Triggers layout
anime({
targets: '.element',
left: '250px',
width: '500px'
})
```
### Batch Similar Animations
```javascript
// ✅ Single animation for multiple targets
anime({
targets: '.multiple-elements',
translateX: 250
})
// ❌ Avoid: Multiple separate animations
elements.forEach(el => {
anime({ targets: el, translateX: 250 })
})
```
### Use `will-change` for Complex Animations
```css
.animated-element {
will-change: transform, opacity;
}
```
### Disable autoplay for Scroll Animations
```javascript
const animation = anime({
targets: '.element',
translateX: 250,
autoplay: false // Control manually
})
```
## Common Pitfalls
### 1. Forgetting Unit Types
```javascript
// ❌ Wrong: No unit
anime({ targets: '.element', width: 200 })
// ✅ Correct: Include unit
anime({ targets: '.element', width: '200px' })
```
### 2. Using CSS transform Property Directly
```javascript
// ❌ Wrong: Can't animate transform string
anime({ targets: '.element', transform: 'translateX(250px)' })
// ✅ Correct: Individual transform properties
anime({ targets: '.element', translateX: 250 })
```
### 3. Not Handling Animation Cleanup
```javascript
// ❌ Wrong: Animation continues after unmount
useEffect(() => {
anime({ targets: ref.current, translateX: 250 })
}, [])
// ✅ Correct: Pause on cleanup
useEffect(() => {
const anim = anime({ targets: ref.current, translateX: 250 })
return () => anim.pause()
}, [])
```
### 4. Animating Too Many Elements
```javascript
// ❌ Avoid: Animating 1000+ elements
anime({ targets: '.many-items', translateX: 250 }) // 1000+ elements
// ✅ Better: Use CSS animations for large sets
// Or reduce element count with virtualization
```
### 5. Incorrect Timeline Timing
```javascript
// ❌ Wrong: Missing offset operator
.add({ targets: '.el2' }, '500') // Treated as absolute time
// ✅ Correct: Use relative operators
.add({ targets: '.el2' }, '-=500') // Relative to previous
.add({ targets: '.el3' }, '+=200') // Relative to previous
```
### 6. Overusing Loop
```javascript
// ❌ Avoid: Infinite loops drain battery
anime({
targets: '.element',
rotate: '1turn',
loop: true,
duration: 1000
})
// ✅ Better: Use CSS animations for infinite loops
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
```
## Resources
### Scripts
- `animation_generator.py` - Generate Anime.js animation boilerplate (8 types)
- `timeline_builder.py` - Build complex timeline sequences
### References
- `api_reference.md` - Complete Anime.js API documentation
- `stagger_guide.md` - Stagger utilities and patterns
- `timeline_guide.md` - Timeline sequencing deep dive
### Assets
- `starter_animejs/` - Vanilla JS + Vite template with examples
- `examples/` - Real-world patterns (SVG morphing, stagger grids, timelines)
## Related Skills
- **gsap-scrolltrigger** - More powerful timeline features and scroll integration
- **motion-framer** - React-specific declarative animations
- **react-spring-physics** - Physics-based spring animations
- **lightweight-3d-effects** - Simple 3D effects (Zdog, Vanta.js)
**Anime.js vs GSAP**: Use Anime.js for SVG-heavy animations, simpler projects, or when bundle size matters. Use GSAP for complex scroll-driven experiences, advanced timelines, and professional-grade control.
**Anime.js vs Framer Motion**: Use Anime.js for framework-agnostic projects or when working outside React. Use Framer Motion for React-specific declarative animations with gesture integration.