Anime.js is a lightweight JavaScript animation engine — anime({ targets: ".box", translateX: 250, duration: 1000, easing: "easeInOutQuad" }) animates CSS transforms on DOM elements. anime.timeline({ easing: "easeOutExpo" }) sequences animations with .add({ targets, ... }, offset). delay: anime.stagger(100) staggers animations across multiple targets. loop: true and direction: "alternate" create ping-pong loops. SVG path drawing uses strokeDashoffset with the element’s path length. anime.set(targets, { scale: 0 }) applies instant transforms. animation.seek(timeMs) scrubs to a point. onUpdate: (anim) => console.log(anim.progress) tracks percentage. anime.get(el, "translateX") reads current animated values. Anime.js targets CSS classes, id strings, DOM nodes, and JavaScript objects for number interpolation. Claude Code generates Anime.js scroll-triggered counters, SVG logo reveals, page transition sequences, staggered card entrances, and data value animations.
CLAUDE.md for Anime.js
## Anime.js Stack
- Version: animejs >= 3.2 (@types/animejs for TS)
- Basic: anime({ targets: ref.current, translateY: [-20, 0], opacity: [0, 1], duration: 600, easing: "easeOutQuart" })
- Timeline: const tl = anime.timeline({ easing: "easeOutExpo", duration: 500 }); tl.add({ targets, properties }).add({ targets }, "+=100")
- Stagger: delay: anime.stagger(80, { start: 200, from: "center" })
- Cleanup: const anim = anime(...); return () => anim.pause()
- SVG path: const path = el.getTotalLength(); anime({ targets: el, strokeDashoffset: [path, 0] })
- Object: anime({ targets: counter, count: [0, value], round: 1, update: () => setDisplay(Math.round(counter.count)) })
React Hooks
// hooks/useAnime.ts — Anime.js with React refs and cleanup
import { useEffect, useRef, useCallback } from "react"
import anime, { type AnimeParams, type AnimeInstance } from "animejs"
// One-shot animation on mount
export function useAnimeOnMount(params: Omit<AnimeParams, "targets">) {
const ref = useRef<HTMLElement>(null)
const instanceRef = useRef<AnimeInstance | null>(null)
useEffect(() => {
if (!ref.current) return
instanceRef.current = anime({
targets: ref.current,
...params,
})
return () => {
instanceRef.current?.pause()
instanceRef.current = null
}
}, []) // eslint-disable-line react-hooks/exhaustive-deps
return ref
}
// Imperative animation trigger
export function useAnime<T extends HTMLElement = HTMLElement>(
params: Omit<AnimeParams, "targets">,
) {
const ref = useRef<T>(null)
const instanceRef = useRef<AnimeInstance | null>(null)
const play = useCallback(() => {
instanceRef.current?.pause()
if (!ref.current) return
instanceRef.current = anime({
targets: ref.current,
...params,
})
}, [params])
const pause = useCallback(() => instanceRef.current?.pause(), [])
const reverse = useCallback(() => instanceRef.current?.reverse(), [])
const seek = useCallback((ms: number) => instanceRef.current?.seek(ms), [])
useEffect(() => () => { instanceRef.current?.pause() }, [])
return { ref, play, pause, reverse, seek }
}
// Staggered list animation
export function useAnimeStagger(
containerRef: React.RefObject<HTMLElement>,
selector: string,
params: Omit<AnimeParams, "targets">,
) {
const instanceRef = useRef<AnimeInstance | null>(null)
const animate = useCallback((from: typeof params["from"]) => {
if (!containerRef.current) return
const els = containerRef.current.querySelectorAll(selector)
if (!els.length) return
instanceRef.current?.pause()
instanceRef.current = anime({
targets: els,
...params,
delay: anime.stagger(60, { from }),
})
}, [containerRef, selector, params])
useEffect(() => () => { instanceRef.current?.pause() }, [])
return animate
}
Timeline Page Transition
// components/animation/PageReveal.tsx — coordinated page entrance
"use client"
import { useEffect, useRef } from "react"
import anime from "animejs"
interface PageRevealProps {
children: React.ReactNode
className?: string
}
export function PageReveal({ children, className }: PageRevealProps) {
const containerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const container = containerRef.current
if (!container) return
// Reset initial states
anime.set(container.querySelectorAll("[data-reveal='heading']"), {
opacity: 0, translateY: 40,
})
anime.set(container.querySelectorAll("[data-reveal='subheading']"), {
opacity: 0, translateY: 30,
})
anime.set(container.querySelectorAll("[data-reveal='item']"), {
opacity: 0, translateY: 20, scale: 0.97,
})
anime.set(container.querySelectorAll("[data-reveal='fade']"), {
opacity: 0,
})
const tl = anime.timeline({
easing: "easeOutQuart",
})
tl.add({
targets: container.querySelectorAll("[data-reveal='heading']"),
opacity: [0, 1],
translateY: [40, 0],
duration: 700,
})
.add({
targets: container.querySelectorAll("[data-reveal='subheading']"),
opacity: [0, 1],
translateY: [30, 0],
duration: 600,
}, "-=400")
.add({
targets: container.querySelectorAll("[data-reveal='item']"),
opacity: [0, 1],
translateY: [20, 0],
scale: [0.97, 1],
duration: 500,
delay: anime.stagger(80),
}, "-=300")
.add({
targets: container.querySelectorAll("[data-reveal='fade']"),
opacity: [0, 1],
duration: 400,
}, "-=200")
return () => tl.pause()
}, [])
return (
<div ref={containerRef} className={className}>
{children}
</div>
)
}
// Usage:
// <PageReveal>
// <h1 data-reveal="heading">Welcome</h1>
// <p data-reveal="subheading">Subtitle</p>
// <Card data-reveal="item" />
// <Card data-reveal="item" />
// <Footer data-reveal="fade" />
// </PageReveal>
SVG Path Drawing
// components/animation/SvgReveal.tsx — SVG stroke drawing animation
"use client"
import { useEffect, useRef } from "react"
import anime from "animejs"
interface SvgRevealProps {
paths: string[] // SVG d attributes
viewBox?: string
strokeColor?: string
strokeWidth?: number
duration?: number
className?: string
}
export function SvgReveal({
paths,
viewBox = "0 0 100 100",
strokeColor = "#3B82F6",
strokeWidth = 2,
duration = 1500,
className,
}: SvgRevealProps) {
const svgRef = useRef<SVGSVGElement>(null)
useEffect(() => {
const svg = svgRef.current
if (!svg) return
const pathEls = svg.querySelectorAll("path")
const anims: ReturnType<typeof anime>[] = []
pathEls.forEach((path, i) => {
const length = path.getTotalLength()
anime.set(path, {
strokeDasharray: length,
strokeDashoffset: length,
opacity: 1,
})
anims.push(
anime({
targets: path,
strokeDashoffset: [length, 0],
easing: "easeInOutSine",
duration,
delay: i * 200,
}),
)
})
return () => anims.forEach(a => a.pause())
}, [duration])
return (
<svg ref={svgRef} viewBox={viewBox} className={className} fill="none">
{paths.map((d, i) => (
<path
key={i}
d={d}
stroke={strokeColor}
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
opacity={0}
/>
))}
</svg>
)
}
Animated Counter
// components/animation/AnimatedCounter.tsx — number count-up
"use client"
import { useEffect, useRef } from "react"
import anime from "animejs"
interface AnimatedCounterProps {
value: number
prefix?: string
suffix?: string
duration?: number
easing?: string
className?: string
}
export function AnimatedCounter({
value,
prefix = "",
suffix = "",
duration = 1500,
easing = "easeOutExpo",
className,
}: AnimatedCounterProps) {
const displayRef = useRef<HTMLSpanElement>(null)
const counterRef = useRef({ count: 0 })
useEffect(() => {
const el = displayRef.current
if (!el) return
const anim = anime({
targets: counterRef.current,
count: [0, value],
duration,
easing,
round: 1,
update: () => {
el.textContent = prefix + Math.round(counterRef.current.count).toLocaleString() + suffix
},
})
return () => anim.pause()
}, [value, duration, easing, prefix, suffix])
return (
<span ref={displayRef} className={className}>
{prefix}0{suffix}
</span>
)
}
For the GSAP alternative when professional-grade animation with ScrollTrigger, timeline scrubbing, morphing, DrawSVG plugin, and production reliability for complex marketing sites is needed — GSAP has better browser compatibility, more plugins, and smoother performance than Anime.js for advanced use cases, although it requires a commercial license for some plugins, see the GSAP guide. For the react-spring alternative when physics-based spring animations that respond to user gestures and feel natural without specifying explicit duration are preferred — react-spring’s spring physics produces different motion character than Anime.js’s easing curves, see the react-spring guide. The Claude Skills 360 bundle includes Anime.js skill sets covering timelines, stagger, and SVG animations. Start with the free tier to try JavaScript animation generation.