patternjavascriptTip
Intersection Observer for Scroll-Triggered Reveal Animations
Viewed 0 times
IntersectionObserverscroll revealanimate on scrollon screen animationviewport animationlazy animation
Problem
Triggering animations when elements scroll into view via scroll event listeners is expensive due to constant main-thread work.
Solution
Use
IntersectionObserver to watch elements and add an animation class when they enter the viewport. Disconnect the observer after the element has been seen once if the animation should not repeat.const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('animate-in');
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.1 }
);
document.querySelectorAll('.reveal').forEach((el) => observer.observe(el));
// React hook
function useInView(ref, options = {}) {
const [inView, setInView] = useState(false);
useEffect(() => {
const obs = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) { setInView(true); obs.disconnect(); }
}, options);
if (ref.current) obs.observe(ref.current);
return () => obs.disconnect();
}, []);
return inView;
}Why
IntersectionObserver runs off the main thread and batches intersection events, making it far more performant than polling scroll position with
addEventListener('scroll').Gotchas
- Set
rootMarginto trigger animations slightly before elements enter the viewport:rootMargin: '0px 0px -100px 0px'triggers 100px before the bottom edge. - IntersectionObserver fires asynchronously — do not expect synchronous reads of
isIntersectingimmediately afterobserve(). - Elements with
display: noneare never intersecting; ensure elements are visible when observed. - Combine with
prefers-reduced-motionto skip animations for users who prefer less motion.
Revisions (0)
No revisions yet.