통합검색

Javascript

[Javascript] fullpage (GSAP)

  • 2025.08.18 08:55:51
[!]HTML[/!]
 
<aside id="fullpage_dots">
    <button type="button">메뉴1</button>
    <button type="button">메뉴2</button>
    <button type="button">메뉴3</button>
    <button type="button">메뉴4</button>
</aside>
<article id="fullpage_wrapper">
   
    <section id="m1" class="fullpage_sec">
            <!-- visual ( type1: 위치 / type2: 이미지,동영상구분 ) -->
            <div class="visual swiper">
                <div class="roll swiper-wrapper">
                    <?php for ($i=0; $i < 3; $i++) { ?>
                    <div class="item swiper-slide">
                        <video autoplay muted loop playsinline>
                            <source src="<?=DIR?>/files/main<?=$i+1?>.mp4" type="video/mp4">
                        </video>
                    </div>
                    <?php } ?>
                </div>
                <div class="dots"></div>
            </div>
    </section>
    <section id="m2" class="fullpage_sec">
            <h6>main2</h6>
    </section>
    <section id="m3" class="fullpage_sec">
            <h6>main3</h6>
    </section>
    <section id="m4" class="fullpage_sec">
            <h6>main4</h6>
    </section>
    <section id="m5" class="fullpage_sec h_auto">
            <!-- s:footer -->
            <?php include_once PATH."/inc/foot.php"; ?>
            <!-- e:footer -->
    </section>
   
</article>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', () => {
    visual.init();
    main_fullpage.init();
    main_motion.init();
    get_init_progress_bar.init();
});
</script>




[!]CSS[/!]
 
#fullpage_dots {position: fixed;z-index: 33;left: 30px;top: 50%;transform: translateY(-50%);display: flex;flex-direction: column;align-items: center;justify-content: center;}
#fullpage_dots > button {border: none;margin: 5px;}
#fullpage_wrapper {overflow: hidden;}
#fullpage_wrapper .fullpage_sec {height: 100vh;min-height: 50vh;}
#fullpage_wrapper .fullpage_sec.h_auto {height: auto;}
#fullpage_wrapper .fullpage_sec h6 {display: block;align-content: center;text-align: center;width: 100vw;height: 100vh;font-size: 30px;color: #222;font-weight: 700;}
#fullpage_wrapper .fullpage_sec:nth-child(odd) h6 {background: #999;color: #fff;}




[!]Javascript[/!]
const main_fullpage = {
    init : function() {
        this.action();
    },
    action : function() {
        gsap.registerPlugin(ScrollToPlugin);
        const sections = document.querySelectorAll('.fullpage_sec');
        const dots = document.querySelectorAll('#fullpage_dots button');
        if (!sections.length) {
            console.warn('fullpage sections not found');
            return;
        }
        const BREAKPOINT = 1500;
        let currentIndex = 0;
        let isAnimating = false;
        let fullpageActive = true;
        let wheelLock = false;
        function moveToSection(index, updateHash = true) {
            if (index < 0 || index >= sections.length) return;
            if (isAnimating) return;
            isAnimating = true;
            currentIndex = index;
            gsap.killTweensOf(window);
            const target = sections[index];
            const isAuto = target.classList.contains('h_auto');
            gsap.to(window, {
                duration: isAuto ? 1.5 : 0.6,
                scrollTo: target.offsetTop,
                ease: isAuto ? "power2.out" : "power3.out",
                onComplete: () => {
                isAnimating = false;
                }
            });
            dots.forEach(btn => btn.classList.remove('active'));
            if (dots[index]) dots[index].classList.add('active');
            if (updateHash) {
                history.replaceState(null, null, '#' + target.id);
            }
        }
        function handleWheel(e) {
            if (!fullpageActive) return;
           
            e.preventDefault();
            if (isAnimating || wheelLock) return;
            wheelLock = true;
            if (e.deltaY > 0) {
                moveToSection(currentIndex + 1);
            } else {
                moveToSection(currentIndex - 1);
            }
            // 800ms 후 휠 다시 허용
            setTimeout(() => {
                wheelLock = false;
            }, 800);
        }
        function checkResponsive() {
            if (window.innerWidth <= BREAKPOINT) {
                fullpageActive = false;
                window.removeEventListener('wheel', handleWheel);
            } else {
                fullpageActive = true;
                window.addEventListener('wheel', handleWheel, { passive:false });
            }
            // resize
            if (fullpageActive && sections[currentIndex]) {
                gsap.set(window, {
                    scrollTo: sections[currentIndex].offsetTop
                });
            }
        }
        function handleInitialHash() {
            const hash = window.location.hash;
            if (!hash) return;
            const target = document.querySelector(hash);
            if (!target) return;
            const index = Array.from(sections).indexOf(target);
            if (index !== -1) {
                currentIndex = index;
                gsap.set(window, { scrollTo: target.offsetTop });
            }
        }
        dots.forEach((btn, index) => {
            btn.addEventListener('click', () => {
                moveToSection(index);
            });
        });
        window.addEventListener('resize', checkResponsive);
        checkResponsive();
        handleInitialHash();
        console.log('fullpage init complete');
    }
};