# Anime.js 4.0 React Patterns Complete Anime.js 4.0 patterns for React. Lightweight alternative for simple animations. ## Table of Contents 1. [Installation & Setup](#installation--setup) 2. [React Integration](#react-integration) 3. [Basic Animations](#basic-animations) 4. [Timeline](#timeline) 5. [Stagger](#stagger) 6. [Scroll Animations](#scroll-animations) 7. [Text Animations](#text-animations) 8. [SVG Animations](#svg-animations) 9. [Draggable](#draggable) 10. [v3 to v4 Migration](#v3-to-v4-migration) ## Installation & Setup ```bash npm install animejs ``` ### Imports (v4 Syntax) ```tsx // Named imports (v4) import { animate, createTimeline, createScope, createSpring, createDraggable, stagger, svg, utils, engine } from 'animejs' ``` ## React Integration ### Basic Pattern with createScope ```tsx 'use client' import { useEffect, useRef } from 'react' import { animate, createScope } from 'animejs' export function AnimatedBox() { const rootRef = useRef(null) const scopeRef = useRef | null>(null) useEffect(() => { // Create scope bound to container scopeRef.current = createScope({ root: rootRef.current }).add(() => { // All animations here are scoped to rootRef animate('.box', { translateX: 250, rotate: '1turn', duration: 800, ease: 'out(3)', }) }) // Cleanup on unmount return () => scopeRef.current?.revert() }, []) return (
Animated
) } ``` ### Registering Methods for External Control ```tsx 'use client' import { useEffect, useRef } from 'react' import { animate, createScope } from 'animejs' export function ControlledAnimation() { const rootRef = useRef(null) const scopeRef = useRef | null>(null) useEffect(() => { scopeRef.current = createScope({ root: rootRef.current }).add((self) => { // Register method accessible outside useEffect self.add('animateBox', () => { animate('.box', { scale: [1, 1.2, 1], duration: 400, ease: 'out(2)', }) }) }) return () => scopeRef.current?.revert() }, []) const handleClick = () => { // Call registered method scopeRef.current?.methods.animateBox() } return (
Click to animate
) } ``` ## Basic Animations ### Simple Animation ```tsx // v4 syntax: animate(targets, { properties }) animate('.element', { translateX: 250, translateY: 100, rotate: '1turn', scale: 1.5, opacity: 0.5, duration: 1000, ease: 'out(3)', }) ``` ### From/To Values ```tsx animate('.element', { translateX: [0, 250], // from 0 to 250 opacity: [0, 1], // from 0 to 1 scale: [0.5, 1], // from 0.5 to 1 duration: 800, }) ``` ### Keyframes ```tsx animate('.element', { keyframes: [ { translateX: 0, scale: 1 }, { translateX: 100, scale: 1.2 }, { translateX: 200, scale: 1 }, { translateX: 250, scale: 0.8 }, ], duration: 2000, ease: 'inOut(2)', }) ``` ### Property-Specific Parameters ```tsx animate('.element', { translateX: { to: 250, duration: 1000, ease: 'out(4)', }, rotate: { to: '1turn', duration: 1500, ease: 'inOut(2)', }, scale: { to: 1.5, duration: 800, delay: 200, }, }) ``` ### Callbacks ```tsx animate('.element', { translateX: 250, duration: 1000, onBegin: () => console.log('Started'), onUpdate: (anim) => console.log(anim.progress), onComplete: () => console.log('Done'), onLoop: () => console.log('Loop'), }).then(() => { console.log('Promise resolved') }) ``` ### Playback Controls ```tsx const animation = animate('.element', { translateX: 250, autoplay: false, }) animation.play() // Play forward animation.pause() // Pause animation.resume() // Resume in current direction animation.reverse() // Play backward animation.restart() // Restart from beginning animation.seek(500) // Seek to 500ms animation.reset() // Reset to initial state ``` ## Timeline ### Basic Timeline ```tsx import { createTimeline } from 'animejs' const tl = createTimeline({ defaults: { duration: 500, ease: 'out(3)', } }) tl.add('.box-1', { translateX: 250 }) .add('.box-2', { translateX: 250 }, '-=200') // 200ms before previous ends .add('.box-3', { translateX: 250 }, '+=100') // 100ms after previous ends ``` ### Timeline in React ```tsx 'use client' import { useEffect, useRef } from 'react' import { createTimeline, createScope } from 'animejs' export function TimelineAnimation() { const rootRef = useRef(null) const scopeRef = useRef | null>(null) useEffect(() => { scopeRef.current = createScope({ root: rootRef.current }).add(() => { const tl = createTimeline({ defaults: { duration: 600, ease: 'out(3)' } }) tl.add('.title', { opacity: [0, 1], translateY: [30, 0] }) .add('.subtitle', { opacity: [0, 1], translateY: [20, 0] }, '-=400') .add('.cta', { opacity: [0, 1], scale: [0.9, 1] }, '-=300') }) return () => scopeRef.current?.revert() }, []) return (

Title

Subtitle

) } ``` ## Stagger ### Basic Stagger ```tsx import { stagger } from 'animejs' animate('.grid-item', { opacity: [0, 1], translateY: [50, 0], delay: stagger(100), // 100ms between each duration: 600, }) ``` ### Stagger with Start Value ```tsx animate('.item', { translateX: 250, delay: stagger(100, { start: 500 }), // Start at 500ms, then +100ms each }) ``` ### Grid Stagger ```tsx animate('.grid-item', { scale: [0, 1], delay: stagger(50, { grid: [4, 4], // 4x4 grid from: 'center', // Animate from center outward }), }) ``` ### Stagger from Index ```tsx animate('.item', { opacity: [0, 1], delay: stagger(100, { from: 'first', // 'first', 'last', 'center', or index number }), }) ``` ### Value Stagger ```tsx animate('.bar', { scaleY: stagger([0.5, 1]), // Scale from 0.5 to 1 distributed duration: 800, }) ``` ## Scroll Animations ### ScrollObserver ```tsx 'use client' import { useEffect, useRef } from 'react' import { animate, createScope } from 'animejs' export function ScrollReveal() { const rootRef = useRef(null) const scopeRef = useRef | null>(null) useEffect(() => { scopeRef.current = createScope({ root: rootRef.current }).add(() => { // Using Intersection Observer pattern const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { animate(entry.target, { opacity: [0, 1], translateY: [50, 0], duration: 800, ease: 'out(3)', }) observer.unobserve(entry.target) } }) }, { threshold: 0.2 } ) document.querySelectorAll('.reveal-item').forEach((el) => { observer.observe(el) }) }) return () => scopeRef.current?.revert() }, []) return (
{[1, 2, 3, 4].map((i) => (
Item {i}
))}
) } ``` ## Text Animations ### Character Split Animation ```tsx 'use client' import { useEffect, useRef } from 'react' import { animate, createScope, stagger } from 'animejs' export function TextReveal({ text }: { text: string }) { const rootRef = useRef(null) const scopeRef = useRef | null>(null) useEffect(() => { scopeRef.current = createScope({ root: rootRef.current }).add(() => { animate('.char', { opacity: [0, 1], translateY: [50, 0], rotateX: [-90, 0], delay: stagger(30), duration: 600, ease: 'out(3)', }) }) return () => scopeRef.current?.revert() }, []) return (
{text.split('').map((char, i) => ( {char === ' ' ? '\u00A0' : char} ))}
) } ``` ### Word Animation ```tsx export function WordReveal({ text }: { text: string }) { const rootRef = useRef(null) const scopeRef = useRef | null>(null) useEffect(() => { scopeRef.current = createScope({ root: rootRef.current }).add(() => { animate('.word', { opacity: [0, 1], translateY: ['100%', '0%'], delay: stagger(80), duration: 800, ease: 'out(4)', }) }) return () => scopeRef.current?.revert() }, []) return (
{text.split(' ').map((word, i) => ( {word} ))}
) } ``` ## SVG Animations ### Path Drawing ```tsx import { svg } from 'animejs' export function DrawSVG() { const svgRef = useRef(null) const scopeRef = useRef | null>(null) useEffect(() => { scopeRef.current = createScope({ root: svgRef.current }).add(() => { const drawable = svg.createDrawable('.draw-path') animate(drawable, { draw: ['0 0', '0 1'], // From 0% to 100% duration: 2000, ease: 'inOut(2)', }) }) return () => scopeRef.current?.revert() }, []) return ( ) } ``` ### SVG Morphing ```tsx import { svg } from 'animejs' export function MorphSVG() { const pathRef = useRef(null) useEffect(() => { const morph = svg.createMorph(pathRef.current) animate(morph, { to: 'M50,10 A40,40 0 1,1 50,90 A40,40 0 1,1 50,10', // Circle duration: 1000, ease: 'inOut(2)', }) }, []) return ( ) } ``` ### Motion Path ```tsx import { svg } from 'animejs' export function MotionPath() { const elementRef = useRef(null) useEffect(() => { const motionPath = svg.createMotionPath('#motion-path') animate(elementRef.current, { translateX: motionPath.x, translateY: motionPath.y, rotate: motionPath.angle, duration: 3000, ease: 'linear', loop: true, }) }, []) return ( <>
) } ``` ## Draggable ### Basic Draggable ```tsx import { createDraggable } from 'animejs' export function DraggableBox() { const boxRef = useRef(null) useEffect(() => { const draggable = createDraggable(boxRef.current, { trigger: boxRef.current, releaseEase: 'out(3)', releaseStiffness: 50, }) return () => draggable.revert() }, []) return (
Drag me
) } ``` ### Draggable with Constraints ```tsx createDraggable(element, { container: containerRef.current, // Constrain to container x: { min: 0, max: 500 }, // X bounds y: { min: 0, max: 300 }, // Y bounds snap: { x: 50, y: 50 }, // Snap to grid }) ``` ## Easing Reference (v4) ### Built-in Easings ```tsx // v4 easing syntax (no 'ease' prefix) ease: 'linear' ease: 'in(2)' // Power in ease: 'out(2)' // Power out (default) ease: 'inOut(2)' // Power in-out ease: 'out(4)' // Stronger ease out ``` ### Spring Easing ```tsx import { createSpring } from 'animejs' const spring = createSpring({ mass: 1, stiffness: 100, damping: 10, velocity: 0, }) animate('.element', { translateX: 250, ease: spring, }) ``` ### Custom Easing ```tsx // Cubic bezier ease: 'cubicBezier(0.76, 0, 0.24, 1)' // Custom function ease: (t) => t * t // Quadratic ``` ## v3 to v4 Migration | v3 | v4 | |----|----| | `anime({ targets, ...props })` | `animate(targets, { ...props })` | | `easing: 'easeOutQuad'` | `ease: 'out(2)'` | | `easing: 'easeInOutCubic'` | `ease: 'inOut(3)'` | | `endDelay` | `loopDelay` | | `direction: 'reverse'` | `reversed: true` | | `direction: 'alternate'` | `alternate: true` | | `update` callback | `onUpdate` callback | | `begin` callback | `onBegin` callback | | `complete` callback | `onComplete` callback | | `.finished.then()` | `.then()` | | `anime.timeline()` | `createTimeline()` | | `anime.stagger()` | `stagger()` | ## When to Use Anime.js vs GSAP | Use Anime.js | Use GSAP | |--------------|----------| | Simple animations | Complex scroll-driven | | Lightweight needs (~17kb) | ScrollTrigger required | | Quick prototypes | Production timelines | | SVG morphing | SplitText, MorphSVG | | Draggable elements | Pin sections | ## Sources - [Anime.js Documentation](https://animejs.com/documentation/) - [Using with React](https://animejs.com/documentation/getting-started/using-with-react/) - [v3 to v4 Migration Guide](https://github.com/juliangarnier/anime/wiki/Migrating-from-v3-to-v4)