Javascript
[GSAP] fullpage 대체 효과 구현
- 2025.11.05 08:32:38
![]() 바닐라스크립트 + GSAP 환경에서 jquery 의 fullpage 효과를 대체하는 효과. GSAP 의 ScrollToPlugin 플러그인을 활용해 구현 가능하다. 코드가 제공하는 기능은 아래와 같다. - 마우스 스크롤시 섹션 자동 이동 - 모바일에서는 fullpage 효과 해제 (리사이징시에도 적용) - 섹션 이동시 주소표시줄에 해시(#) 적용 - 해시로 접속한 경우 해당 섹션 자동으로 이동 - navigation 적용 [!]HTML 코드[/!] <!-- 컨텐츠 섹션 -->
<div id="content"> <div class="panel">첫번째 섹션</div> <div class="panel">두번째 섹션</div> <div class="panel">세번째 섹션</div> </div> <!-- navigation --> <div id="pagination"> <ul> <li><a href="#"><p>첫번째 섹션으로</p></a></li> <li><a href="#"><p>두번째 섹션으로</p></a></li> <li><a href="#"><p>세번째 섹션으로</p></a></li> </ul> </div> [!]Javascript(GSAP) 코드[/!] ////////////////////
// fullpage 대체 효과 //////////////////// gsap.registerPlugin(ScrollToPlugin); const panels = document.querySelectorAll('.panel'); const navLinks = document.querySelectorAll('#pagination a'); // 1200px 이상에서만 작동되도록 let isDesktop = window.matchMedia('(min-width: 1200px)').matches; const mql = window.matchMedia('(min-width: 1200px)'); let isScrolling = false; let currentIndex = 0; // 해시로 진입했을 때 처리 (#1, #2 ...) initFromHash(); // 뷰포트 변경 시 데스크톱 여부 갱신 mql.addEventListener('change', (e) => { isDesktop = e.matches; }); // 스크롤시 navigation 메뉴 active 처리 및 location.hash 처리 function setActive(idx) { navLinks.forEach(a => a.classList.remove('on')); if (navLinks[idx]) navLinks[idx].classList.add('on'); const hash = '#' + (idx + 1); if (location.hash !== hash) { // replaceState 쌓이지 않도록 history.replaceState(null, '', hash); } } // 해시로 들어왔을 때 초기 위치 맞추기 function initFromHash() { const h = location.hash.replace('#', ''); const num = parseInt(h, 10); if (!isNaN(num) && num >= 1 && num <= panels.length) { currentIndex = num - 1; gsap.set(window, { scrollTo: panels[currentIndex] }); setActive(currentIndex); } else { setActive(0); } } // pc에서만 휠로 섹션 이동 window.addEventListener('wheel', (e) => { if (!isDesktop) return; if (isScrolling) return; e.preventDefault(); if (e.deltaY > 0 && currentIndex < panels.length - 1) { currentIndex++; } else if (e.deltaY < 0 && currentIndex > 0) { currentIndex--; } else { return; } scrollToPanel(currentIndex); }, { passive: false }); // navigation 메뉴 클릭 navLinks.forEach((a, i) => { a.addEventListener('click', (e) => { e.preventDefault(); if (isScrolling || i === currentIndex) return; currentIndex = i; scrollToPanel(currentIndex); }); }); // 스크롤 이동 효과 (gsap) function scrollToPanel(index) { isScrolling = true; if (isDesktop) disableScroll(); gsap.to(window, { duration: 1.1, scrollTo: panels[index], ease: "power3.inOut", onComplete() { setActive(index); setTimeout(() => { isScrolling = false; enableScroll(); }, 200); } }); } // 이동하는 도중에는 스크롤 잠금 function disableScroll() { window.addEventListener('wheel', prevent, { passive: false }); window.addEventListener('touchmove', prevent, { passive: false }); window.addEventListener('keydown', preventForKeys, { passive: false }); } function enableScroll() { window.removeEventListener('wheel', prevent, { passive: false }); window.removeEventListener('touchmove', prevent, { passive: false }); window.removeEventListener('keydown', preventForKeys, { passive: false }); } function prevent(e) { e.preventDefault(); } function preventForKeys(e) { const keys = [32,33,34,35,36,37,38,39,40]; if (keys.includes(e.keyCode)) e.preventDefault(); } |

