Anime.js Scroll Animations — Complete Guide with Examples
In this tutorial, you'll learn about Anime.js scroll animations. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
Anime.js scroll animations trigger and control animation progress based on the browser's scroll position, creating parallax effects, scroll-triggered reveals, and progress-driven motion.
What You'll Learn
By the end of this tutorial, you'll use Anime's scroll detection, create parallax scrolling effects, animate elements when they enter the viewport, and synchronize animation progress with scroll position.
Why It Matters
Scroll animations make pages feel alive and interactive. They guide the user's attention, tell a visual story, and create a premium browsing experience. Properly implemented scroll animations increase engagement and time on page.
Real-World Use
Doda Browser's landing page uses Anime.js scroll animations for feature reveals. As the user scrolls, feature cards fade in and slide up, a progress bar fills, and background elements move at different speeds for a parallax depth effect.
Where This Fits in Your Learning Path
flowchart LR
A["SVG & Morph"] --> B["**Scroll Animations**"]
B --> C["Performance Optimization"]
C --> D["Anime.js Project"]
D --> E["Advanced Anime.js"]
style B fill:#f97316,stroke:#c2410c,color:#fff
style A fill:#e5e7eb,stroke:#9ca3af,color:#374151
style E fill:#22c55e,stroke:#16a34a,color:#fff
Basic Scroll-Triggered Animation
Use the Intersection Observer API or a scroll event listener to trigger Anime animations.
const elements = document.querySelectorAll('.animate-on-scroll')
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
anime({
targets: entry.target,
translateY: [50, 0],
opacity: [0, 1],
duration: 800,
easing: 'easeOutQuad'
})
observer.unobserve(entry.target)
}
})
})
elements.forEach(el => observer.observe(el))
Expected output: As each element scrolls into view, it slides up from 50px below and fades in. The animation plays once.
Parallax Scrolling
Create parallax effects by mapping scroll position to animation properties.
window.addEventListener('scroll', () => {
const scrollY = window.scrollY
anime({
targets: '.parallax-bg',
translateY: scrollY * 0.3,
duration: 1,
easing: 'linear'
})
anime({
targets: '.parallax-fg',
translateY: scrollY * 0.1,
duration: 1,
easing: 'linear'
})
})
Expected output: The background moves slower than the foreground as the user scrolls, creating a depth illusion.
Progress-Based Animation
Link animation progress directly to scroll position for a reading progress indicator.
const progressBar = document.querySelector('.progress-bar')
window.addEventListener('scroll', () => {
const scrollProgress = window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)
anime({
targets: progressBar,
scaleX: scrollProgress,
duration: 1,
easing: 'linear'
})
})
Expected output: A progress bar at the top of the page fills from left to right as the user scrolls down.
Staggered Scroll Reveals
Reveal multiple elements with staggered timing as they enter the viewport.
const cards = document.querySelectorAll('.card')
const observer = new IntersectionObserver((entries) => {
const visibleCards = entries.filter(e => e.isIntersecting)
visibleCards.forEach((entry, i) => {
anime({
targets: entry.target,
translateY: [60, 0],
opacity: [0, 1],
delay: i * 150,
duration: 600,
easing: 'easeOutQuad'
})
observer.unobserve(entry.target)
})
})
cards.forEach(card => observer.observe(card))
Expected output: Cards stagger into view as the user scrolls, each appearing 150ms after the previous one.
Scroll-Controlled Timeline
Use scroll position to control a timeline's progress, creating a story-driven scroll experience.
const tl = anime.timeline({
duration: 1,
autoplay: false
})
tl.add({ targets: '.scene-1', opacity: [0, 1], scale: [0.8, 1] })
tl.add({ targets: '.scene-2', opacity: [0, 1], translateX: [-100, 0] })
tl.add({ targets: '.scene-3', opacity: [0, 1], rotate: ['-10deg', '0deg'] })
window.addEventListener('scroll', () => {
const progress = window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)
tl.seek(tl.duration * progress)
})
Expected output: Scrolling down advances the timeline through three scenes. Each scene animates in order as the user scrolls.
Common Mistakes
1. Creating new animations on every scroll event
Creating anime() on every scroll event is expensive. Use a single persistent animation and update its progress, or use duration: 1 for instant application.
2. Not throttling scroll event handlers
Scroll events fire rapidly. Use requestAnimationFrame or a throttle to limit updates to once per frame.
3. Forgetting to unobserve elements after animation
Continuously observing animated elements wastes resources. Call observer.unobserve after the animation triggers.
4. Using scroll animations on mobile without testing
Mobile scroll behavior differs from desktop. Test touch scroll, momentum scrolling, and address bar hide/show effects.
5. Animating layout properties during scroll
Animating width, height, or top triggers layout recalculations. Stick to transforms and opacity for scroll-driven animations.
Practice Questions
How do you detect when an element enters the viewport? Use the Intersection Observer API to watch for visibility changes.
What is parallax scrolling? Different layers move at different speeds as the user scrolls, creating a 3D depth illusion.
How do you link animation progress to scroll position? Calculate scroll percentage and use anime.seek() or duration: 1 with the progress value.
Why should you throttle scroll event handlers? Scroll events fire at high frequency. Throttling prevents excessive animation creation and frame drops.
What properties are safest to animate during scroll? Transforms (translate, scale, rotate) and opacity. Avoid layout-triggering properties.
Challenge
Build a story page with three scenes. Each scene triggers a unique animation (fade, slide, rotate) when it scrolls into view. The animations should only play once.
FAQ
Mini Project
Build a scroll-driven story page. Three content sections animate in sequence as the user scrolls. A progress bar at the top shows reading progress. Background colors transition between sections.
const sections = document.querySelectorAll('.story-section')
const progressBar = document.querySelector('.scroll-progress')
const bgColors = ['#ff6b6b', '#4ecdc4', '#a29bfe', '#ffe66d']
function updateScroll() {
const scrollY = window.scrollY
const winHeight = window.innerHeight
const docHeight = document.documentElement.scrollHeight
const maxScroll = docHeight - winHeight
const progress = Math.min(scrollY / maxScroll, 1)
anime({
targets: progressBar,
scaleX: progress,
duration: 1,
easing: 'linear'
})
sections.forEach((section, i) => {
const rect = section.getBoundingClientRect()
const isVisible = rect.top < winHeight - 100 && rect.bottom > 100
if (isVisible) {
document.body.style.backgroundColor = bgColors[i]
const entryProgress = 1 - (rect.bottom / winHeight)
anime({
targets: section.querySelector('.content'),
translateY: [50 - entryProgress * 50, 0],
opacity: [Math.min(entryProgress * 2, 1), 1],
duration: 1
})
}
})
}
window.addEventListener('scroll', updateScroll, { passive: true })
<style>.story-section { min-height: 100vh; display: flex; align-items: center; justify-content: center; } .content { opacity: 0; } .scroll-progress { position: fixed; top: 0; left: 0; width: 100%; height: 4px; background: #333; transform-origin: left; } .scroll-progress span { display: block; height: 100%; background: #4ecdc4; transform: scaleX(0); }</style>
<div class="scroll-progress"><span style="display:block;height:100%;background:#4ecdc4;transform:scaleX(0)"></span></div>
<section class="story-section"><div class="content"><h2>Chapter 1: Discovery</h2><p>Our journey begins at dawn.</p></div></section>
<section class="story-section"><div class="content"><h2>Chapter 2: Challenge</h2><p>Obstacles appear on the path.</p></div></section>
<section class="story-section"><div class="content"><h2>Chapter 3: Triumph</h2><p>Victory is achieved at last.</p></div></section>
What's Next
Optimize your animations for production:
| Tutorial | What You'll Learn |
|---|---|
| Performance Optimization | Tips for smooth 60fps animations |
| Anime.js Project | Build a complete production-ready animation |
Related topics: Intersection Observer API, requestAnimationFrame.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro