https://www.youtube.com/watch?v=bP7iGE3YKOU&list=PL4UVBBIc6giL8-6jvrClimg0cFL-Muqiq&index=4
https://webstoryboy.co.kr/1911
이 예제에서 3번 snap에 대한 이해를 하기 위해 하루를 씨름함.
<script>
/*
✅ GSAP ScrollTrigger + snap 기능 완벽 정리 ✅
✔ 각 섹션에 ScrollTrigger를 적용하고 순차적으로 고정
✔ snap 기능을 활용해 부드럽게 스크롤 이동
✔ 코드마다 설명 + 궁금했던 부분까지 주석으로 정리!
*/
// ① 모든 섹션(.parallax__item) 가져오기
let panels = gsap.utils.toArray(".parallax__item");
// ② 각 섹션의 ScrollTrigger 객체 저장 (snap 기능에서 활용 예정)
let tops = panels.map(panel => ScrollTrigger.create({
trigger: panel,
start: "top top" // 각 섹션이 화면 최상단에 닿을 때 트리거됨
}));
/*
✅ tops 배열에는 각 섹션의 ScrollTrigger 객체가 저장됨
tops = [
{ start: 값1, ... }, // section1
{ start: 값2, ... }, // section2
{ start: 값3, ... }, // section3
...
]
✔ ScrollTrigger.create() 실행 후 반환된 객체를 map()을 이용해 배열로 저장한 것!
✔ 이 값들은 snap 기능에서 활용할 예정 (현재까지는 사용 X)
*/
// ③ 각 섹션을 개별적으로 고정 (pin: true)
panels.forEach((panel, i) => {
ScrollTrigger.create({
trigger: panel,
start: () => panel.offsetHeight < window.innerHeight ? "top top" : "bottom bottom", // 높이에 따라 다르게 설정
pin: true, // 스크롤할 때 해당 섹션을 고정
pinSpacing: false // 고정된 섹션이 다음 섹션과 겹쳐지지 않도록 함
});
});
/*
✅ start 조건 설명
✔ 현재 브라우저의 viewport height (innerHeight)이 800px이라고 가정
✔ 만약 섹션 높이 (offsetHeight)가 1000px이면 → start: "bottom bottom" 적용
✔ 만약 섹션 높이가 600px이면 → start: "top top" 적용
*/
// ④ snap 기능 적용
ScrollTrigger.create({
snap: {
snapTo: (_, self) => {
let panelStarts = tops.map(st => st.start); // tops 배열에서 start 값만 추출
let snapScroll = gsap.utils.snap(panelStarts, self.scroll()); // 가장 가까운 값으로 스냅 적용
return gsap.utils.normalize(0, ScrollTrigger.maxScroll(window), snapScroll); // 0~1 사이의 비율로 변환
},
duration: 0.5 // 스냅 애니메이션 지속 시간
}
});
/*
✅ snap 기능 설명
✔ snapTo: (_, self) => { ... }
→ progress 값은 사용하지 않으므로 _로 무시 가능
→ self는 ScrollTrigger 자체를 가리킴 (this 사용 불가)
✔ let panelStarts = tops.map(st => st.start);
→ tops 배열에서 start 값만 추출 (예: ["top top", "top top", ...])
→ snap 기능에서 어느 위치로 이동할지 결정하는 기준이 됨
✔ let snapScroll = gsap.utils.snap(panelStarts, self.scroll());
→ 현재 스크롤 위치(self.scroll())에서 가장 가까운 panelStarts 값으로 스냅 적용
✔ return gsap.utils.normalize(0, ScrollTrigger.maxScroll(window), snapScroll);
→ 전체 스크롤 길이(0 ~ 최대 스크롤 값)를 0~1 사이의 값으로 변환
→ 퍼센트 단위로 변환해야 부드러운 스냅 효과 가능 (그렇지 않으면 딱딱하게 이동)
*/
</script>