
Observer는 기본 API라 브라우저 수준에서 최적화됨
등장 후 class만 붙이는 단순 작업이라면 GSAP 사용보단 Observer 사용하는게 더 빠름
수백 개 DOM 요소에도 렉이 거의 없음
자동 스로틀링 내장 → scroll 직접 감지보다 CPU 부담 작음
많은 요소 감지해야 하는 상황에서 ScrollTrigger보다 안정적
GSAP는 복잡한 연동, 위치 제어, 타임라인 컨트롤 등 필요할 때 사용
GSAP이 이미 들어가 있어도
간단한 진입 트리거는 IntersectionObserver로 따로 처리하는 게 더 나음

html
<div class="container">
<div class="img"><img src="./fdsfd.png" /></div>
<div class="img"><img src="./fdsfd.png" /></div>
<div class="img"><img src="./fdsfd.png" /></div>
<div class="img"><img src="./fdsfd.png" /></div>
<div class="img"><img src="./fdsfd.png" /></div>
<div class="img"><img src="./fdsfd.png" /></div>
</div>
css
* {
margin: 0;
padding: 0;
}
.container {
display: flex;
flex-direction: column;
gap: 100px;
width: 50%;
margin: 0 auto;
height: auto;
}
.img {
width: 100%;
opacity: 0;
visibility: hidden;
transform: translateY(60px);
transition: all 0.6s ease;
}
img {
width: 100%;
height: auto;
vertical-align: bottom;
}
.img.active {
opacity: 1;
transform: translateY(0);
visibility: visible;
}
js
const scrollTrigger = () => {
const imgs = document.querySelectorAll('.img');
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('active');
observer.unobserve(entry.target); // 한 번만 트리거
} else {
entry.target.classList.remove('active');
}
});
}, {
root: null,
threshold: 0.3, // 요소가 30% 이상 보이면 발동
});
imgs.forEach(img => observer.observe(img));
};
window.addEventListener('DOMContentLoaded', () => {
requestAnimationFrame(() => {
window.scrollTo(0, 0); // 처음 스크롤 위치 때문에 사용
});
scrollTrigger();
});