技術ブログ

【vanilla js】GSAPを使わずにスクロール途中で横スクロールさせる方法
2024.07.31

【vanilla js】GSAPを使わずにスクロール途中で横スクロールさせる方法

2024.07.31 9

See the Pen 【vanilla js】GSAPを使わずにスクロール途中で横スクロールさせる方法 by WP TechLab. (@wptech-lab-com) on CodePen.

<section class="section sec1">
    <p>sec1</p>
</section>
<section class="section sec2">
    <div class="scroll_slide_container">
        <div class="scroll_container_wrapper">
            <div class="scroll_container">
                <div class="scroll_item">item1</div>
                <div class="scroll_item">item2</div>
                <div class="scroll_item">item3</div>
                <div class="scroll_item">item4</div>
                <div class="scroll_item">item5</div>
                <div class="scroll_item">item6</div>
                <div class="scroll_item">item7</div>
                <div class="scroll_item">item8</div>
                <div class="scroll_item">item9</div>
                <div class="scroll_item">item10</div>
            </div>
        </div>
    </div>
</section>
<section class="section sec3">
    <p>sec3</p>
</section>
<section class="section sec2">
    <div class="scroll_slide_container">
        <div class="scroll_container_wrapper">
            <div class="scroll_container">
                <div class="scroll_item">item1</div>
                <div class="scroll_item">item2</div>
                <div class="scroll_item">item3</div>
                <div class="scroll_item">item4</div>
                <div class="scroll_item">item5</div>
                <div class="scroll_item">item6</div>
                <div class="scroll_item">item7</div>
                <div class="scroll_item">item8</div>
                <div class="scroll_item">item9</div>
                <div class="scroll_item">item10</div>
                <div class="scroll_item">item11</div>
                <div class="scroll_item">item12</div>
                <div class="scroll_item">item13</div>
                <div class="scroll_item">item14</div>
                <div class="scroll_item">item15</div>
                <div class="scroll_item">item16</div>
                <div class="scroll_item">item17</div>
                <div class="scroll_item">item18</div>
                <div class="scroll_item">item19</div>
                <div class="scroll_item">item20</div>
            </div>
        </div>
    </div>
</section>
<section class="section sec3">
    <p>sec5</p>
</section>
* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

.section {
    display: block;
    width: 100%;
    overflow-x: clip;
}

.sec1,
.sec3 {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
    background-color: #eee;
}

.scroll_slide_container {
    position: relative;
    display: flex;
    align-items: flex-start;
    width: 100%;
    color: #fff;
    min-height: 100vh;
}

.scroll_container_wrapper {
    position: sticky;
    top: 0;
    left: 0;
    right: 0;
    display: flex;
    align-items: center;
    width: 100%;
    height: 100vh;
}

.scroll_container {
    display: flex;
    align-items: flex-start;
    gap: 20px;
    width: 100%;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
    height: 400px;
}

.scroll_item {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 600px;
    height: 400px;
    flex-shrink: 0;
    background-color: #191919;
}
document.addEventListener("DOMContentLoaded", () => {
    initializeSliders();
    setInitialScrollPositions();
});

const initializeSliders = () => {
    const scrollSlideContainers = document.querySelectorAll('.scroll_slide_container');

    scrollSlideContainers.forEach(scrollSlideContainer => {
        const scrollContainerWrapper = scrollSlideContainer.querySelector('.scroll_container_wrapper');
        const scrollContainer = scrollSlideContainer.querySelector('.scroll_container');

        const scrollSlideContainerTop = scrollSlideContainer.getBoundingClientRect().top + window.pageYOffset;
        const WindowWidth = window.innerWidth;
        const WindowHeight = window.innerHeight;
        const WindowHalfHeight = WindowHeight / 2;
        const scrollContainerWrapperHeight = scrollContainerWrapper.offsetHeight;
        const scrollContainerWidth = scrollContainer.scrollWidth;

        scrollSlideContainer.style.height = `${scrollContainerWidth + (window.innerWidth - document.body.clientWidth)}px`;

        let rootMargin = `-${(WindowHalfHeight) - (scrollContainerWrapperHeight / 2)}px 0px`;

        const observerOptions = {
            root: null,
            rootMargin: rootMargin,
            threshold: [1]
        };

        let scrollListenerAdded = false;
        let ticking = false;

        const handleScroll = () => {
            if (!ticking) {
                window.requestAnimationFrame(() => {
                    let translateX = scrollSlideContainerTop - window.scrollY + (window.innerWidth - document.body.clientWidth);
                    translateX = Math.min(0, Math.max(translateX, -(scrollContainerWidth - WindowWidth + (window.innerWidth - document.body.clientWidth))));
                    scrollContainer.style.transform = `translateX(${translateX}px)`;
                    ticking = false;
                });
                ticking = true;
            }
        };

        const observer = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.intersectionRatio === 1) {
                    scrollContainer.style.position = "fixed";
                    if (!scrollListenerAdded) {
                        scrollListenerAdded = true;
                        window.addEventListener('scroll', handleScroll);
                    }
                } else {
                    if (scrollListenerAdded) {
                        scrollContainer.style.position = "";
                        scrollListenerAdded = false;
                        window.removeEventListener('scroll', handleScroll);
                    }
                }
            });
        }, observerOptions);

        observer.observe(scrollContainerWrapper);

        setInitialScrollPosition(scrollSlideContainer, scrollContainer, scrollSlideContainerTop, scrollContainerWidth, WindowWidth);
    });
};

const setInitialScrollPosition = (scrollSlideContainer, scrollContainer, scrollSlideContainerTop, scrollContainerWidth, WindowWidth) => {
    const initialScrollPosition = window.scrollY;
    if (initialScrollPosition >= scrollSlideContainerTop && initialScrollPosition <= (scrollSlideContainerTop + scrollContainerWidth - WindowWidth)) {
        let translateX = scrollSlideContainerTop - initialScrollPosition + (window.innerWidth - document.body.clientWidth);
        translateX = Math.min(0, Math.max(translateX, -(scrollContainerWidth - WindowWidth + (window.innerWidth - document.body.clientWidth))));
        scrollContainer.style.transform = `translateX(${translateX}px)`;
    } else if (initialScrollPosition > (scrollSlideContainerTop + scrollContainerWidth - WindowWidth)) {
        let translateX = -(scrollContainerWidth - WindowWidth + (window.innerWidth - document.body.clientWidth));
        scrollContainer.style.transform = `translateX(${translateX}px)`;
    }
};

const setInitialScrollPositions = () => {
    const scrollSlideContainers = document.querySelectorAll('.scroll_slide_container');

    scrollSlideContainers.forEach(scrollSlideContainer => {
        const scrollContainer = scrollSlideContainer.querySelector('.scroll_container');
        const scrollSlideContainerTop = scrollSlideContainer.getBoundingClientRect().top + window.pageYOffset;
        const scrollContainerWidth = scrollContainer.scrollWidth;
        const WindowWidth = window.innerWidth;

        setInitialScrollPosition(scrollSlideContainer, scrollContainer, scrollSlideContainerTop, scrollContainerWidth, WindowWidth);
    });
};