• 카테고리

    질문 & 답변
  • 세부 분야

    프론트엔드

  • 해결 여부

    미해결

main.js 를 적용한 후에 show-scene-1에서 2로 넘아기질 않습니다..ㅠㅠ

21.02.01 12:48 작성 조회수 154

0

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="preconnect" href="https://fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;900&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="default.css">
    <link rel="stylesheet" href="01.css">
</head>
<body>
    <div class="container">
        <nav class="global-nav">
            <div class="global-nav-links">
                <a href="#" class="global-nav-item">Rooms</a>
                <a href="#" class="global-nav-item">Ideas</a>
                <a href="#" class="global-nav-item">Stores</a>
                <a href="#" class="global-nav-item">Contact</a>
            </div>
        </nav>
        <nav class="local-nav">
            <div class="local-nav-links">
                <a href="#" class="product-name">AirMug Pro</a>
                <a href="#" class="local-nav-item">개요</a>
                <a href="#" class="local-nav-item">제품사양</a>
                <a href="#" class="local-nav-item">구입하기</a>
            </div>
        </nav>
        <section class="scroll-section" id="scroll-section-0">
            <h1>AirMug Pro</h1>
            <div class="sticky-elem main-message a">
                <p>온전히 빠져들게 하는<br>최고급 세라믹</p>
            </div>
            <div class="sticky-elem main-message b">
                <p>주변 맛을 느끼게 해주는<br>주변 맛 허용 모드</p>
            </div>
            <div class="sticky-elem main-message c">
                <p>온종일 편안한<br>맞춤형 손잡이</p>
            </div>
            <div class="sticky-elem main-message d">
                <p>새롭게 입가를<br>찾아온 매혹</p>
            </div>
        </section>
        <section class="scroll-section" id="scroll-section-1">
            <p class="description">
                <strong >보통 스크롤 영역</strong>
                Lorem ipsum dolor sit amet consectetur adipisicing elit. Blanditiis optio quasi maxime quam dolore sequi, beatae non qui dolorum magni aspernatur. Temporibus id debitis accusantium non porro. Veritatis, magnam saepe. Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nobis, debitis vero aliquam facilis soluta, facere, quis magnam ex deleniti dicta reprehenderit ad necessitatibus veritatis quas repellendus nemo tempore molestiae dignissimos.

            </p>
        </section>
        <section class="scroll-section" id="scroll-section-2">
            <div class="sticky-elem main-message a">
                <p>
                    <small>편안한 촉감</small>
                    입과 하나되다
                </p>
            </div>
            <div class="sticky-elem desc-message b">
                <p>
                    편안한 목넘김을 완성하는 디테일한 여러 구성 요소들, 우리는 이를 하나하나 새롭게 살피고 재구성하는 과정을 거쳐 새로운 수준의 머그, AirMug Pro를 만들었습니다. 입에 뭔가 댔다는 감각은 어느새 사라지고 오롯이 당신과 음료만 남게 되죠.
                </p>
                <div class="pin"></div>
            </div>
            <div class="sticky-elem desc-message c">
                <p>
                    디자인 앤 퀄리티 오브 스웨덴,<br>메이드 인 차이나
                </p>
                <div class="pin"></div>
            </div>
        </section>
        <section class="scroll-section" id="scroll-section-3">
            <p class="mid-message">
                <strong>Retina 머그</strong><br>
                아이디어를 광활하게 펼칠<br>
                아름답고 부드러운 음료 공간.
            </p>
            <p class="canvas-caption">
                Lorem ipsum dolor sit amet consectetur adipisicing elit. Assumenda natus totam molestias sequi possimus ducimus nisi id. Odit, mollitia consequuntur voluptatum eius doloremque, aperiam, porro tenetur officiis ex illum nisi! Lorem ipsum dolor sit amet consectetur, adipisicing elit. Dolorum ducimus nemo harum iusto consequatur, ipsam necessitatibus nulla, tempora nobis, nam sunt nostrum accusantium atque. Itaque quas veniam eum molestiae nesciunt!
            </p>
        </section>
        <footer class="footer">
            2020, 최태호
        </footer>
    </div>
    <script src="js/01.js"></script>
</body>
</html>

(()=> {
    let yOffset = 0; //window.pageYOffset 대신 쓸 변수
    let prevScrollHeight = 0; //현재 스크롤 위치(yoffset)보다 이전에 위치한 스크롤 섹션들의 스크롤 높이값의 합
    let currentScene = 0; //현재 활성된(눈앞에 보고 있는)씬(scroll-section)
    let enterNewScene = false; //새로운 씬이 시작된 순간 트루로 바뀔 예정

    const sceneInfo = [
        {   
            //0
            type: 'sticky',
            heightNum: 5, //브라우저 높이의 5배로 scrollHeight를 세팅(디바이스의 높이의 5배를 하기 위해 설정)
            scrollHeight: 0,
            objs: {
                container: document.querySelector("#scroll-section-0"),
                messageA: document.querySelector('#scroll-section-0 .main-message.a'),
                messageB: document.querySelector('#scroll-section-0 .main-message.b'),
                messageC: document.querySelector('#scroll-section-0 .main-message.c'),
                messageD: document.querySelector('#scroll-section-0 .main-message.d'),
            },
            values: {
                messageA_opacity_in: [0, 1, { start: 0.1, end: 0.2 }],
                messageB_opacity_in: [0, 1, { start: 0.3, end: 0.4 }],
                messageC_opacity_in: [0, 1, { start: 0.5, end: 0.6 }],
                messageD_opacity_in: [0, 1, { start: 0.7, end: 0.8 }],
                messageA_translateY_in: [20, 0, { start: 0.1, end: 0.2 }],
                messageB_translateY_in: [20, 0, { start: 0.3, end: 0.4 }],
                messageC_translateY_in: [20, 0, { start: 0.5, end: 0.6 }],
                messageD_translateY_in: [20, 0, { start: 0.7, end: 0.8 }],
                messageA_opacity_out: [1, 0, { start: 0.25, end: 0.3 }],
                messageB_opacity_out: [1, 0, { start: 0.45, end: 0.5 }],
                messageC_opacity_out: [1, 0, { start: 0.65, end: 0.7 }],
                messageD_opacity_out: [1, 0, { start: 0.85, end: 0.9 }],
                messageA_translateY_out: [0, -20, { start: 0.25, end: 0.3 }],
                messageB_translateY_out: [0, -20, { start: 0.45, end: 0.5 }],
                messageC_translateY_out: [0, -20, { start: 0.65, end: 0.7 }],
                messageD_translateY_out: [0, -20, { start: 0.85, end: 0.9 }]
            }
        },
        {
            //1
            type: 'normal',
            heightNum: 5, 
            scrollHeight: 0,
            objs: {
                container: document.querySelector("#scroll-section-1"),
                content: document.querySelector('#scroll-section-1 .description')
            }
        },
        {
            //2
            type: 'sticky',
            heightNum: 5, 
            scrollHeight: 0,
            objs: {
                container: document.querySelector('#scroll-section-2'),
                messageA: document.querySelector('#scroll-section-2 .a'),
                messageB: document.querySelector('#scroll-section-2 .b'),
                messageC: document.querySelector('#scroll-section-2 .c'),
                pinB: document.querySelector('#scroll-section-2 .b .pin'),
                pinC: document.querySelector('#scroll-section-2 .c .pin')
            },
            values: {
                messageA_translateY_in: [20, 0, { start: 0.15, end: 0.2 }],
                messageB_translateY_in: [30, 0, { start: 0.6, end: 0.65 }],
                messageC_translateY_in: [30, 0, { start: 0.87, end: 0.92 }],
                messageA_opacity_in: [0, 1, { start: 0.25, end: 0.3 }],
                messageB_opacity_in: [0, 1, { start: 0.6, end: 0.65 }],
                messageC_opacity_in: [0, 1, { start: 0.87, end: 0.92 }],
                messageA_translateY_out: [0, -20, { start: 0.4, end: 0.45 }],
                messageB_translateY_out: [0, -20, { start: 0.68, end: 0.73 }],
                messageC_translateY_out: [0, -20, { start: 0.95, end: 1 }],
                messageA_opacity_out: [1, 0, { start: 0.4, end: 0.45 }],
                messageB_opacity_out: [1, 0, { start: 0.68, end: 0.73 }],
                messageC_opacity_out: [1, 0, { start: 0.95, end: 1 }],
                pinB_scaleY: [0.5, 1, { start: 0.6, end: 0.65 }],
                pinC_scaleY: [0.5, 1, { start: 0.87, end: 0.92 }]
            }
        },
        {
            //3
            type: 'sticky',
            heightNum: 5,
            scrollHeight: 0,
            objs: {
                container: document.querySelector("#scroll-section-3")
            }
        }
    ];
    function setLayout() {
        // 각 스크롤 섹션의 높이 세팅
        for (let i=0; i< sceneInfo.length; i++){
            if (sceneInfo[i].type === "sticky") {
                sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight;
            } else if (sceneInfo[i].type === "normal") {
                sceneInfo[i].scrollHeight = sceneInfo[i].objs.container.yOffsetHeight + window.innerHeight * 0.5;
            }
            sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`;
        }

        yOffset = window.pageYOffset;
        let totalScrollHeight = 0;
        for (let i = 0; i < sceneInfo.length; i++) {
            totalScrollHeight += sceneInfo[i].scrollHeight;
            if (totalScrollHeight >= yOffset) {
                currentScene = i;
                break;
            }
        }
        document.body.setAttribute('id', `show-scene-${currentScene}`);
    }
    
    function calcValues (values, currentYOffset) {
        let rv;
        // 현재 씬(스크롤섹션)에서 스크롤된 범위를 비율로 구하기
        const scrollHeight = sceneInfo[currentScene].scrollHeight;
        const scrollRatio = currentYOffset / sceneInfo[currentScene].scrollHeight;
        
        if (values.length === 3) {
            // start ~ end 사이에 애니메이션 실행
            const partScrollStart = values[2].start * scrollHeight;
            const partScrollEnd = values[2].end * scrollHeight;
            const partScrollHeight = partScrollEnd - partScrollStart;

            if (currentYOffset >= partScrollStart && currentYOffset <= partScrollEnd) {
                rv = (currentYOffset - partScrollStart) / partScrollHeight * (values[1] - values[0]) + values[0]; 
            } else if (currentYOffset < partScrollStart){
                rv = values[0];
            } else if (currentYOffset > partScrollEnd) {
                rv = values[1];
            }

        } else {
            rv = scrollRatio * (values[1] - values[0]) + values[0]; 
        }
        console.log(rv)
        return rv;
    }

    function playAnimation() {
        const objs = sceneInfo[currentScene].objs;
        const values = sceneInfo[currentScene].values;
        const currentYOffset = yOffset- prevScrollHeight;
        const scrollHeight = sceneInfo[currentScene].scrollHeight;
        const scrollRatio = currentYOffset / scrollHeight;

        switch (currentScene) {
            case 0:
                if (scrollRatio <= 0.22) {
                    // in
                    objs.messageA.style.opacity = calcValues(values.messageA_opacity_in, currentYOffset);
                    objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_in, currentYOffset)}%, 0)`;
                } else {
                    // out
                    objs.messageA.style.opacity = calcValues(values.messageA_opacity_out, currentYOffset);
                    objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_out, currentYOffset)}%, 0)`;
                }
    
                if (scrollRatio <= 0.42) {
                    // in
                    objs.messageB.style.opacity = calcValues(values.messageB_opacity_in, currentYOffset);
                    objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_in, currentYOffset)}%, 0)`;
                } else {
                    // out
                    objs.messageB.style.opacity = calcValues(values.messageB_opacity_out, currentYOffset);
                    objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_out, currentYOffset)}%, 0)`;
                }
    
                if (scrollRatio <= 0.62) {
                    // in
                    objs.messageC.style.opacity = calcValues(values.messageC_opacity_in, currentYOffset);
                    objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_in, currentYOffset)}%, 0)`;
                } else {
                    // out
                    objs.messageC.style.opacity = calcValues(values.messageC_opacity_out, currentYOffset);
                    objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_out, currentYOffset)}%, 0)`;
                }
    
                if (scrollRatio <= 0.82) {
                    // in
                    objs.messageD.style.opacity = calcValues(values.messageD_opacity_in, currentYOffset);
                    objs.messageD.style.transform = `translate3d(0, ${calcValues(values.messageD_translateY_in, currentYOffset)}%, 0)`;
                } else {
                    // out
                    objs.messageD.style.opacity = calcValues(values.messageD_opacity_out, currentYOffset);
                    objs.messageD.style.transform = `translate3d(0, ${calcValues(values.messageD_translateY_out, currentYOffset)}%, 0)`;
                }

                break;
            case 1:
                // let messageA_opacity_in = calcValues(values.messageA_opacity, currentYOffset);
                // objs.messageA.style.opacity = messageA_opacity_in;
                break;
            case 2:
                // console.log("2 play");
                if (scrollRatio <= 0.32) {
                    // in
                    objs.messageA.style.opacity = calcValues(values.messageA_opacity_in, currentYOffset);
                    objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_in, currentYOffset)}%, 0)`;
                } else {
                    // out
                    objs.messageA.style.opacity = calcValues(values.messageA_opacity_out, currentYOffset);
                    objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_out, currentYOffset)}%, 0)`;
                }
    
                if (scrollRatio <= 0.67) {
                    // in
                    objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_in, currentYOffset)}%, 0)`;
                    objs.messageB.style.opacity = calcValues(values.messageB_opacity_in, currentYOffset);
                    objs.pinB.style.transform = `scaleY(${calcValues(values.pinB_scaleY, currentYOffset)})`;
                } else {
                    // out
                    objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_out, currentYOffset)}%, 0)`;
                    objs.messageB.style.opacity = calcValues(values.messageB_opacity_out, currentYOffset);
                    objs.pinB.style.transform = `scaleY(${calcValues(values.pinB_scaleY, currentYOffset)})`;
                }
    
                if (scrollRatio <= 0.93) {
                    // in
                    objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_in, currentYOffset)}%, 0)`;
                    objs.messageC.style.opacity = calcValues(values.messageC_opacity_in, currentYOffset);
                    objs.pinC.style.transform = `scaleY(${calcValues(values.pinC_scaleY, currentYOffset)})`;
                } else {
                    // out
                    objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_out, currentYOffset)}%, 0)`;
                    objs.messageC.style.opacity = calcValues(values.messageC_opacity_out, currentYOffset);
                    objs.pinC.style.transform = `scaleY(${calcValues(values.pinC_scaleY, currentYOffset)})`;
                }
                
                break;
            case 3:
                // console.log("3 play");
                break;
        }
    }

    function scrollLoop() {
        //전체 스크롤(타임라인 합)의 누적을 막기 위해 0으로 설정
        enterNewScene = false;
        prevScrollHeight = 0;
        
        for(let i=0; i <currentScene; i++){
            prevScrollHeight = prevScrollHeight + sceneInfo[i].scrollHeight;
        }

        if (yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) {
            enterNewScene = true;
            currentScene++;
            document.body.setAttribute('id', `show-scene-${currentScene}`);

        }
        if (yOffset < prevScrollHeight) {
            enterNewScene = true;
            if (currentScene === 0) return; //브라우저 바운스 효과로 인해 마이너스가 되는 것을 방지(모바일)
            currentScene--;
            document.body.setAttribute('id', `show-scene-${currentScene}`);
        }

        if (enterNewScene) return;
        
        playAnimation();
    
    } 

    window.addEventListener("scroll", () =>{
        yOffset= window.pageYOffset;
        scrollLoop();
    });

    
    
    window.addEventListener('load', setLayout);
    window.addEventListener('resize', setLayout);
})();

답변 1

답변을 작성해보세요.

0

아이고, 제가 질문을 늦게 발견해서 답이 늦었네요~~^^;

function setLayout() 에서 콘텐츠 높이를 가져오는 속성 이름이 잘못 지정되어 제대로 동작을 안하고 있습니다.

sceneInfo[i].scrollHeight = sceneInfo[i].objs.container.yOffsetHeight + window.innerHeight * 0.5;

위 부분에서 yOffsetHeight를 offsetHeight로 바꾸어주세요~