스크롤 기반 애니메이션

Scroll-driven animation

JS 없이 CSS만으로 스크롤 진행률을 애니메이션의 타임라인으로 직접 연결하는 최신 기능. animation-timeline이 핵심입니다.

다른 이름: CSS scroll-timelineanimation-timeline: scroll()
···
html
<div class="demo">
  <div class="scroller" id="scroller">
    <div class="content">
      <div class="mark">●</div><div class="mark">●</div><div class="mark">●</div><div class="mark">●</div><div class="mark">●</div>
    </div>
  </div>
  <div class="bartrack"><div class="bar" id="bar"></div></div>
  <code class="label">animation-timeline: scroll()</code>
</div>
css
.demo{position:absolute;inset:8%;display:grid;grid-template-rows:1fr auto auto;gap:10px}
.scroller{position:relative;overflow-y:scroll;border:1px solid var(--line);border-radius:10px;
  scroll-timeline:--st block;scrollbar-width:none}
.scroller::-webkit-scrollbar{display:none}
.content{height:320%;display:flex;flex-direction:column;justify-content:space-between;padding:16px 0}
.mark{color:var(--muted);font-size:12px;text-align:center}
.bartrack{height:8px;border-radius:6px;background:var(--line);overflow:hidden}
.bar{height:100%;width:100%;transform:scaleX(0);transform-origin:left;background:var(--accent);
  animation:grow linear;animation-timeline:--st}
@keyframes grow{from{transform:scaleX(0)}to{transform:scaleX(1)}}
.label{font:10px/1 ui-monospace,monospace;color:var(--muted);text-align:center}
js
const el = document.getElementById('scroller');
const bar = document.getElementById('bar');
let dir = 1;
function loop() {
  const max = el.scrollHeight - el.clientHeight;
  el.scrollTop += dir * 1.2;
  if (el.scrollTop >= max) dir = -1;
  if (el.scrollTop <= 0) dir = 1;
  // 브라우저가 animation-timeline 으로 이미 갱신 중이면 이 값은 같은 결과를 한 번 더 쓸 뿐이고,
  // 아직 지원하지 않는 브라우저에서는 이 값이 유일한 진행 표시가 된다.
  const progress = max > 0 ? el.scrollTop / max : 0;
  bar.style.transform = 'scaleX(' + progress + ')';
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);

기존에는 스크롤에 맞춰 뭔가 움직이려면 scroll 이벤트를 리스닝하고 requestAnimationFrame으로 스타일을 갱신해야 했습니다. animation-timeline은 애니메이션의 진행률 자체를 시간이 아니라 스크롤 위치로 바꿔버립니다 — @keyframes는 그대로 쓰고 재생 헤드만 스크롤바에 묶입니다.

상단 진행바(reading progress bar)는 CSS 몇 줄로 만들 수 있고, view() 타임라인을 쓰면 요소가 뷰포트에 들어오고 나가는 구간만으로 애니메이션 진행률을 만들 수 있습니다(스크롤 리빌의 네이티브 버전).

2026년 기준 Chrome·Edge는 지원하지만 Safari·Firefox는 아직 부분적이라, 실무에서는 여전히 JS 폴백을 함께 둡니다.

언제 쓰나

지원 브라우저가 확인되는 프로젝트에서 진행바·시차 효과를 가볍게 만들 때 씁니다. 폭넓은 지원이 필요하면 JS 스크롤 리스너로 폴백을 준비하세요.