단순히 특정 지점에 도달했을 때 "재생" 버튼을 누르는 것을 넘어, 마우스 휠(스크롤)을 굴리는 속도와 방향에 맞춰 비디오를 탐색하듯 애니메이션의 프레임이 앞뒤로 움직이게 하려면 scrub 옵션을 사용합니다.
또한, 애니메이션이 진행되는 긴 스크롤 구간 동안 현재 화면을 특정 섹션에 묶어두고(고정) 싶을 때는 pin: true를 사용합니다. 이 두 가지를 결합하면 마치 Apple 제품 소개 페이지 같은 역동적인 스토리텔링 웹 페이지를 만들 수 있습니다.
[그림 1] 스크롤바와 타임라인을 동기화하는 Scrub 방식과 뷰포트를 고정하는 Pin 동작 원리
1. 스크롤 인터랙션 핵심 문법
속성
작성 예시
설명
scrub
scrub: true scrub: 1
true: 스크롤바와 애니메이션 진행률이 완전히 1:1로 실시간 동기화됩니다. 숫자(초): 스크롤이 멈춘 후 지정된 시간(초)만큼 지연되며 부드럽게(Smooth) 애니메이션이 따라붙습니다.
pin
pin: true pin: ".container"
지정된 트리거(또는 직접 입력한 요소)를 애니메이션이 종료(end)될 때까지 현재 화면에 고정(position: fixed 처럼 동작)합니다.
end
end: "+=1000"
일반적으로 Pin을 사용할 때는 특정 지점 대신 +=숫자 형식을 사용해 "시작점으로부터 n픽셀을 스크롤하는 동안" 애니메이션을 유지시킵니다.
2. 시각적 원리: Scrub vs Non-Scrub
💡 팁: Scrub 숫자는 왜 필요한가요?
scrub: true를 쓰면 마우스를 멈출 때 애니메이션도 즉각적으로 딱딱하게 멈춥니다. 하지만 scrub: 1처럼 1초 딜레이를 주면, 사용자가 마우스 스크롤을 멈춰도 애니메이션이 1초간 관성처럼 부드럽게 이어지면서 감속하여 멈추기 때문에 훨씬 더 고급스러운 UI UX를 제공할 수 있습니다.
<script>
// [시스템 핫픽스] Iframe Resizer가 내부 스크롤 요소를 측정하지 못하게 차단
const origQSA = document.querySelectorAll;
document.querySelectorAll = function(selector) {
const elements = origQSA.call(this, selector);
if (selector === '*') {
return Array.from(elements).filter(el => !el.closest('.scroll-container'));
}
return elements;
};
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
<div class="scroll-container">
<div class="panel">⬇️ 스크롤 시작 ⬇️</div>
<div class="panel pin-section">
<div class="ghost">👻</div>
<div class="loading-bar">
<div class="progress"></div>
</div>
</div>
<div class="panel">⬆️ 스크롤 끝 ⬆️</div>
</div>