스크롤 리빌

Scroll reveal

요소가 뷰포트에 들어오는 순간 페이드인·슬라이드업 같은 등장 애니메이션을 트리거하는 패턴. IntersectionObserver로 구현합니다.

다른 이름: Reveal on scrollFade-up on scroll
···
html
<div class="viewport" id="vp">
  <div class="track" id="track">
    <div class="row">Row A</div>
    <div class="row">Row B</div>
    <div class="row">Row C</div>
    <div class="row">Row D</div>
    <div class="row">Row A</div>
    <div class="row">Row B</div>
    <div class="row">Row C</div>
    <div class="row">Row D</div>
  </div>
  <div class="line"></div>
</div>
css
.viewport{position:absolute;inset:6%;overflow:hidden;border:1px solid var(--line);border-radius:12px;background:var(--surface)}
.track{display:flex;flex-direction:column;gap:14px;padding:16px}
.row{padding:12px 16px;border-radius:10px;background:var(--bg);border:1px solid var(--line);color:var(--muted);
  font:600 13px/1.3 sans-serif;opacity:0;transform:translateY(24px);transition:opacity .5s ease-out,transform .5s ease-out,border-color .5s,color .5s}
.row.in{opacity:1;transform:translateY(0);color:var(--fg);border-color:var(--accent)}
.line{position:absolute;left:0;right:0;top:58%;height:2px;background:var(--accent-2);opacity:.5}
js
const vp = document.getElementById('vp');
const track = document.getElementById('track');
const io = new IntersectionObserver((entries) => {
  entries.forEach((en) => en.target.classList.toggle('in', en.isIntersecting));
}, { root: vp, threshold: 0.5 });
document.querySelectorAll('.row').forEach((r) => io.observe(r));

let y = 0;
function loop() {
  y -= 0.5;
  if (y < -track.scrollHeight / 2) y = 0;
  track.style.transform = 'translateY(' + y + 'px)';
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);

스크롤 이벤트를 직접 리스닝하지 않고 IntersectionObserver로 요소가 화면에 "보이기 시작"하는 순간만 감지합니다. 콜백에서 클래스를 하나 추가하면 CSS transition이 나머지를 처리합니다.

한 번 보인 요소는 observer.unobserve()로 그만 감시하는 게 일반적입니다 — 스크롤할 때마다 계속 깜빡이면 오히려 산만합니다. threshold(예: 0.5)로 "얼마나 보여야 트리거할지"를 조절합니다.

카드 미리보기는 스크롤이 안 되므로, 이 데모는 가상의 스크롤 컨테이너를 자동으로 위로 흘려보내며 각 행이 관찰 영역에 들어올 때 리빌 애니메이션을 켭니다.

언제 쓰나

콘텐츠가 있다는 걸 보여주는 용도로 쓰고, 정보 자체를 가리는 용도로는 쓰지 마세요 — 스크롤이 느린 사용자나 리더기에는 방해가 됩니다.