Intersection Observer — Web Animation

Intersection Observer untuk Animasi IntersectionObserver API memungkinkan kamu mendeteksi kapan elemen masuk atau keluar dari viewport (atau container…

Intersection Observer untuk Animasi

IntersectionObserver API memungkinkan kamu mendeteksi kapan elemen masuk atau keluar dari viewport (atau container tertentu). Ini sangat berguna untuk scroll-triggered animation, lazy loading, dan infinite scroll.

Membuat Intersection Observer

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      // Elemen terlihat di viewport
      entry.target.classList.add('visible');
    }
  });
});

// Observe elemen
document.querySelectorAll('.animate-on-scroll').forEach((el) => {
  observer.observe(el);
});

Options

const observer = new IntersectionObserver(callback, {
  // root — container yang jadi acuan (default: viewport)
  root: null,

  // rootMargin — margin di sekitar root
  // Positif = trigger lebih awal, negatif = trigger lebih lambat
  rootMargin: '0px 0px -100px 0px',

  // threshold — berapa persen elemen harus terlihat
  // 0 = baru masuk sedikit, 1 = harus full visible
  threshold: 0.1,
});

Threshold

// Trigger saat 10% elemen terlihat
new IntersectionObserver(cb, { threshold: 0.1 });

// Trigger saat 50% elemen terlihat
new IntersectionObserver(cb, { threshold: 0.5 });

// Trigger saat 100% elemen terlihat
new IntersectionObserver(cb, { threshold: 1.0 });

// Multiple thresholds — callback dipanggil di setiap threshold
new IntersectionObserver(cb, {
  threshold: [0, 0.25, 0.5, 0.75, 1.0]
});

rootMargin

// Trigger 200px SEBELUM elemen masuk viewport (preload)
new IntersectionObserver(cb, {
  rootMargin: '200px 0px'
});

// Trigger SETELAH elemen 100px di dalam viewport
new IntersectionObserver(cb, {
  rootMargin: '-100px 0px'
});

Scroll-triggered Animation Pattern

/* State awal — belum terlihat */
.animate-on-scroll {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.6s ease-out, transform 0.6s ease-out;
}

/* State akhir — terlihat */
.animate-on-scroll.visible {
  opacity: 1;
  transform: translateY(0);
}

/* Variasi arah */
.from-left {
  transform: translateX(-50px);
}
.from-left.visible {
  transform: translateX(0);
}

.from-right {
  transform: translateX(50px);
}
.from-right.visible {
  transform: translateX(0);
}

.scale-in {
  transform: scale(0.8);
}
.scale-in.visible {
  transform: scale(1);
}
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
      // Unobserve setelah animasi (opsional)
      observer.unobserve(entry.target);
    }
  });
}, {
  threshold: 0.1,
  rootMargin: '0px 0px -50px 0px',
});

document.querySelectorAll('.animate-on-scroll').forEach((el) => {
  observer.observe(el);
});

Stagger Animation

Beri delay berbeda untuk setiap elemen agar muncul berurutan:

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      // Ambil index dari data attribute
      const delay = entry.target.dataset.delay || 0;
      entry.target.style.transitionDelay = \`${delay}ms\`;
      entry.target.classList.add('visible');
      observer.unobserve(entry.target);
    }
  });
}, { threshold: 0.1 });

// HTML: <div class="animate-on-scroll" data-delay="0">
//       <div class="animate-on-scroll" data-delay="100">
//       <div class="animate-on-scroll" data-delay="200">

Lazy Loading Images

const imageObserver = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      const img = entry.target;
      // Pindahkan src dari data-src ke src
      img.src = img.dataset.src;
      img.classList.add('loaded');
      imageObserver.unobserve(img);
    }
  });
}, {
  rootMargin: '200px 0px', // Load 200px sebelum terlihat
});

document.querySelectorAll('img[data-src]').forEach((img) => {
  imageObserver.observe(img);
});

Cleanup

Jangan lupa disconnect observer saat tidak dibutuhkan lagi:

// Stop observing satu elemen
observer.unobserve(element);

// Stop observing semua elemen
observer.disconnect();

Intersection Observer adalah tool yang powerful dan performant karena callback-nya berjalan secara asynchronous dan tidak memblok main thread, berbeda dengan scroll event listener.

Yang akan kamu pelajari