inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

애플 웹사이트 인터랙션 클론!

video section 영역을 2개 이상 추가 했더니 첫번째 두번째 영역은 나오는데 3번째 영역부터 이미지가 그려지지 않습니다.

141

class

작성한 질문수 1

0

안녕하세요 좋은 강의 감사합니다.
예제 샘플을 이용해서 한가지 해보고 싶은게 있어 테스트 중에 있습니다.
scroll section-0, scroll section-2 구간에 video-canvas-0, video-canvas-1을 main-js 샘플을 만들어 주신것처럼 똑같이 만들었습니다.
추후에 scroll-section-4를 추가 하고 video-canvas-2를 만들어 1,2와 같이 동일하게 만들었는데 3번째 영역이 보여지질 않습니다 ㅜㅜ
어떤 문제가 있는지 알고 싶습니다.
index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>IKEA</title>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;900&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="css/default.css">
    <link rel="stylesheet" href="main.css">
</head>
<body>
    <div class="container">
        <section class="scroll-section" id="scroll-section-0">
            <div class="sticky-elem stick-elem-canvas">
                <canvas id="video-canvas-0" width="1920" height="1080"></canvas>
            </div>
        </section>
        <section class="scroll-section" id="scroll-section-1">
            <p class="description">
                <strong>재료소개</strong>
                재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 
            </p>
        </section>
        <section class="scroll-section" id="scroll-section-2">
            <div class="sticky-elem stick-elem-canvas">
                <canvas id="video-canvas-1" width="1920" height="1080"></canvas>
            </div>
        </section>
        <section class="scroll-section" id="scroll-section-3">
            <p class="description">
                <strong>재료소개</strong>
                재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 
            </p>
        </section>
        <section class="scroll-section" id="scroll-section-4">
            <div class="sticky-elem stick-elem-canvas">
                <canvas id="video-canvas-2" width="1920" height="1080"></canvas>
            </div>
        </section>
        <section class="scroll-section" id="scroll-section-5">
            <p class="description">
                <strong>재료소개</strong>
                재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 재료소개 
                <br/><br/><br/><br/><br/><br/><br/>
            </p>
        </section>
    </div>
    
    <script src="js/main.js"></script>
</body>
</html>
main.css
#show-scene-0 #scroll-section-0 .sticky-elem,
#show-scene-1 #scroll-section-1 .sticky-elem,
#show-scene-2 #scroll-section-2 .sticky-elem,
#show-scene-3 #scroll-section-3 .sticky-elem,
#show-scene-4 #scroll-section-4 .sticky-elem,
#show-scene-5 #scroll-section-5 .sticky-elem {
    display: block;
    will-change: transform, opacity;
}
main.js
(() => {
    let yOffset = 0//window.pageYoffset 대신 쓸 변수
    let prevScrollHeight = 0//현재 스크롤 위치(yOffset)보다 이전에 위치한 스크롤 섹션들의 스크롤 높이값의 합
    let currentScene = 0//현재 활성화된(눈 앞에 보고있는) 씬(scroll-section)
    let enterNewScene = false;  //새로운 scene이 시작된 순간 true
    let acc = 0.1;
    let delayedYOffset = 0;
    let rafId;
    let rafState;

    const sceneInfo = [
        { 
            // 0
            type 'sticky',
            heightNum 8// 브라우저 높이의 5배로 scrollHeight 세팅
            scrollHeight 0,
            objs: {
                container document.querySelector('#scroll-section-0'),
                canvas document.querySelector('#video-canvas-0'),
                context document.querySelector('#video-canvas-0').getContext('2d'),
                videoImages : []
            },
            values : {
                videoImageCount 314,
                imageSequence : [0313],
                canvas_opacity : [10, {start0.9end1}],
            }
        },
        {
            // 1
            type 'normal',
            // heightNum : 5, // 브라우저 높이의 5배로 scrollHeight 세팅 (normal에서는 필요없음)
            scrollHeight 0,
            objs: {
                container document.querySelector('#scroll-section-1')
            }
        },
        {
            // 2
            type 'sticky',
            heightNum 8// 브라우저 높이의 5배로 scrollHeight 세팅
            scrollHeight 0,
            objs: {
                containerdocument.querySelector('#scroll-section-2'),
                canvas document.querySelector('#video-canvas-1'),
                context document.querySelector('#video-canvas-1').getContext('2d'),
                videoImages : []
            },
            values: {
                videoImageCount 471,
                imageSequence : [0470],
                canvas_opacity_in : [01, {start0end0.1}],
                canvas_opacity_out : [10, {start0.95end1}],
            }
        },
        {
            // 3
            type 'normal',
            // heightNum : 5, // 브라우저 높이의 5배로 scrollHeight 세팅 (normal에서는 필요없음)
            scrollHeight 0,
            objs: {
                container document.querySelector('#scroll-section-3')
            }
        },
        {
            // 4
            type 'sticky',
            heightNum 8// 브라우저 높이의 5배로 scrollHeight 세팅
            scrollHeight 0,
            objs: {
                containerdocument.querySelector('#scroll-section-4'),
                canvas document.querySelector('#video-canvas-2'),
                context document.querySelector('#video-canvas-2').getContext('2d'),
                videoImages : []
            },
            values: {
                videoImageCount 338,
                imageSequence : [0337],
                canvas_opacity_in : [01, {start0end0.1}],
                canvas_opacity_out : [10, {start0.95end1}],
            }
        },
        {
            // 5
            type 'normal',
            // heightNum : 5, // 브라우저 높이의 5배로 scrollHeight 세팅 (normal에서는 필요없음)
            scrollHeight 0,
            objs: {
                container document.querySelector('#scroll-section-5')
            }
        },
    ];

    function setCanvasImages() {
        
        let imgElem1;
        for (let i = 1i < sceneInfo[0].values.videoImageCounti++) {
            let filenameNumber = i;
            let filename = '';
            if ( filenameNumber < 10) {
                filename = '000' + filenameNumber;
            } else if ( filenameNumber < 100) {
                filename = '00' + filenameNumber;
            } else if (filenameNumber >= 100 && filenameNumber < 1000) {
                filename = '0' + filenameNumber;
            } else {
                filename = String(filenameNumber);
            }
            imgElem1 = new Image();
            imgElem1.src = `./video/chapter1/img_${filename}.jpg`;
            sceneInfo[0].objs.videoImages.push(imgElem1);
        // 24프레임
        }

        let imgElem2;
        for (let i = 1i < sceneInfo[2].values.videoImageCounti++) {
            let filenameNumber = i;
            let filename = '';
            if ( filenameNumber < 10) {
                filename = '000' + filenameNumber;
            } else if ( filenameNumber < 100) {
                filename = '00' + filenameNumber;
            } else if (filenameNumber >= 100 && filenameNumber < 1000) {
                filename = '0' + filenameNumber;
            } else {
                filename = String(filenameNumber);
            }
            imgElem2 = new Image();
            imgElem2.src = `./video/chapter2/img_${filename}.jpg`;
            sceneInfo[2].objs.videoImages.push(imgElem2);
        // 24프레임
        }

        let imgElem3;
        for (let i = 1i < sceneInfo[4].values.videoImageCounti++) {
            let filenameNumber = i;
            let filename = '';
            if ( filenameNumber < 10) {
                filename = '000' + filenameNumber;
            } else if ( filenameNumber < 100) {
                filename = '00' + filenameNumber;
            } else if (filenameNumber >= 100 && filenameNumber < 1000) {
                filename = '0' + filenameNumber;
            } else {
                filename = String(filenameNumber);
            }
            imgElem3 = new Image();
            imgElem3.src = `./video/chapter3/img_${filename}.jpg`;
            sceneInfo[4].objs.videoImages.push(imgElem3);
        // 24프레임
        }
    }

    function setLayout() {
        // 각 스크롤 섹션의 높이 세팅
        for (let i = 0i < sceneInfo.lengthi++) {
            if (sceneInfo[i].type === 'sticky') {
                sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight;    //각섹션 스크롤 총 높이 = hightnum * 뷰포트 높이
            } else if (sceneInfo[i].type === 'normal') {
                sceneInfo[i].scrollHeight = sceneInfo[i].objs.container.offsetHeight
            }
            sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`;
        }

        yOffset = window.pageYOffset;

        let totalScrollHeight = 0;
        for ( let i = 0i < sceneInfo.lengthi++) {
            totalScrollHeight += sceneInfo[i].scrollHeight;
            iftotalScrollHeight >= yOffset ) {
                currentScene = i;   
                break;
            }
        }
        document.body.setAttribute('id'`show-scene-${currentScene}`);

        // 캔버스에 사이즈 조정
        const heightRatio = window.innerHeight / 1080;
        sceneInfo[0].objs.canvas.style.transform = `translate3d(-50%, -50%, 0) scale(${heightRatio})`;
        sceneInfo[2].objs.canvas.style.transform = `translate3d(-50%, -50%, 0) scale(${heightRatio})`;
        sceneInfo[4].objs.canvas.style.transform = `translate3d(-50%, -50%, 0) scale(${heightRatio})`;
    }

    function calcValues(valuescurrentYOffset) {
        let rv;
        // 현재 씬 (스크롤섹션)에서 스크롤된 범위를 비율로 구하기
        const scrollHeight = sceneInfo[currentScene].scrollHeight;
        let scrollRatio = currentYOffset / 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];
        }

        return rv;
    }

    function playAnimation() {
        const objs = sceneInfo[currentScene].objs;
        const values = sceneInfo[currentScene].values;
        const currentYOffset = yOffset - prevScrollHeight;  //섹션에 현재 yoffset 값 
        const scrollHeight = sceneInfo[currentScene].scrollHeight;
        const scrollRatio = currentYOffset  / scrollHeight

        switch (currentScene) {
            case 0 : 
                // let sequence = Math.round(calcValues(values.imageSequence, currentYOffset));    //math 반올림 처리
                // objs.context.drawImage(objs.videoImages[sequence], 0, 0);
                objs.canvas.style.opacity = calcValues(values.canvas_opacitycurrentYOffset);
                break;
            case 1 : 
                break;
            case 2 : 
                // let sequence2 = Math.round(calcValues(values.imageSequence, currentYOffset));    //math 반올림 처리
                // objs.context.drawImage(objs.videoImages[sequence2], 0, 0);
                if (scrollRatio <= 0.3) {
                    // in
                    objs.canvas.style.opacity = calcValues(values.canvas_opacity_incurrentYOffset);
                } else {
                    // out
                    objs.canvas.style.opacity = calcValues(values.canvas_opacity_outcurrentYOffset);
                }
                break;
            case 3 : 
                break;
            case 4 : 
                // let sequence4 = Math.round(calcValues(values.imageSequence, currentYOffset));    //math 반올림 처리
                // objs.context.drawImage(objs.videoImages[sequence4], 0, 0);
                if (scrollRatio <= 0.3) {
                    // in
                    objs.canvas.style.opacity = calcValues(values.canvas_opacity_incurrentYOffset);
                } else {
                    // out
                    objs.canvas.style.opacity = calcValues(values.canvas_opacity_outcurrentYOffset);
                }
                break;
            case 5 : 
                break;
        }
    }

    function scrollLoop() {
        enterNewScene = false;
        prevScrollHeight = 0;

        for (let i = 0i < currentScenei++) {
            prevScrollHeight += sceneInfo[i].scrollHeight;
        }

        if (delayedYOffset < prevScrollHeight + sceneInfo[currentScene].scrollHeight) {
            document.body.classList.remove('scroll-effect-end');
        }

        if (delayedYOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) {
            enterNewScene = true;
            if (currentScene === sceneInfo.length - 1) {
                document.body.classList.add('scroll-effect-end');
            }
            if (currentScene < sceneInfo.length - 1) {
                currentScene++;
            }
            document.body.setAttribute('id'`show-scene-${currentScene}`);
        }

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

        if (enterNewScenereturn;

        playAnimation();
    }

    function loop() {
        delayedYOffset = delayedYOffset + (yOffset - delayedYOffset* acc;

        if (!enterNewScene) {
            if (currentScene === 0 || currentScene === 2 || currentScene === 4) {
                const currentYOffset = delayedYOffset - prevScrollHeight;
                const objs = sceneInfo[currentScene].objs;
                const values = sceneInfo[currentScene].values;
                let sequence = Math.round(calcValues(values.imageSequencecurrentYOffset));
                if (objs.videoImages[sequence]) {
                    objs.context.drawImage(objs.videoImages[sequence], 00);
                }
            }
        }

        // 일부 기기에서 페이지 끝으로 고속 이동하면 body id가 제대로 인식 안되는 경우를 해결
        // 페이지 맨 위로 갈 경우: scrollLoop와 첫 scene의 기본 캔버스 그리기 수행
        if (delayedYOffset < 1) {
            scrollLoop();
            sceneInfo[0].objs.canvas.style.opacity = 1;
            sceneInfo[0].objs.context.drawImage(sceneInfo[0].objs.videoImages[0], 00);
        }
        // 페이지 맨 아래로 갈 경우: 마지막 섹션은 스크롤 계산으로 위치 및 크기를 결정해야할 요소들이 많아서 1픽셀을 움직여주는 것으로 해결
        if ((document.body.offsetHeight - window.innerHeight- delayedYOffset < 1) {
            let tempYOffset = yOffset;
            scrollTo(0tempYOffset - 1);
        }

        rafId = requestAnimationFrame(loop);

        if (Math.abs(yOffset - delayedYOffset< 1) {
            cancelAnimationFrame(rafId);
            rafState = false;
        }
    }   

    // 페이지 로드 됐을때
    // window.addEventListener('DOMContentLoaded', setLayout);
    window.addEventListener('load', () => {
        setLayout(); // 중간에 새로고침 시, 콘텐츠 양에 따라 높이 계산에 오차가 발생하는 경우를 방지하기 위해 before-load 클래스 제거 전에도 확실하게 높이를 세팅하도록 한번 더 실행
        // document.body.classList.remove('before-load');
        setLayout();
        sceneInfo[0].objs.context.drawImage(sceneInfo[0].objs.videoImages[0], 00);

        // 중간에서 새로고침 했을 경우 자동 스크롤로 제대로 그려주기
        let tempYOffset = yOffset;
        let tempScrollCount = 0;
        if (tempYOffset > 0) {
            let siId = setInterval(() => {
                scrollTo(0tempYOffset);
                tempYOffset += 5;

                if (tempScrollCount > 20) {
                    clearInterval(siId);
                }
                tempScrollCount++;
            }, 20);
        }

         // 스크롤 했을때
        window.addEventListener('scroll', () => {
            yOffset = window.pageYOffset;
            scrollLoop();
            // checkMenu();

            if (!rafState) {
                rafId = requestAnimationFrame(loop);
                rafState = true;
            }
        });

        window.addEventListener('resize', () => {
            if (window.innerWidth > 900) {
              window.location.reload();
          }
        });

        window.addEventListener('orientationchange', () => {
            scrollTo(00);
            setTimeout(() => {
                window.location.reload();
            }, 500);
        });
        // document.querySelector('.loading').addEventListener('transitionend', (e) => {
        //     document.body.removeChild(e.currentTarget);
        // });
    });

    setCanvasImages();
})();
소스는 다음과 같습니다.

웹 디자인 svg HTML/CSS 인터랙티브-웹 javascript 클론코딩

답변 0

이미지 배경 문의

0

82

1

[크로스브라우징] safari에서 동영상 영역 미노출

0

124

1

항상 궁금했는데 크림슨 컬러 선택하셨을때 활용했던 사이트 좀 알려주세요~

0

131

2

vue강의는안하시나요?!

0

125

1

스크롤 속도에 따른 messageA_opacity_out

0

130

1

drawImage(objs.videoImages[sequence], 0, 0); error

0

94

1

선생님 캔버스 width 크기는 이미지 크기에맞게 해줘야하나요?

0

138

0

선생님 안녕하세요. 혹시 메인개발(?)분야가 뭔지 궁금합니다.

0

219

1

React에서 load 상태를 어떻게 감지할 수 있을까요?

0

699

1

[섹션7-3: 버그수정 2] tempYOffset 오류

0

208

1

스크롤할 때 캔버스로 하신 이유가 있으신가요? 그냥 성능 떄문에 캔버스로 하신건가요?

0

329

2

게속 오류떠서 글 작성해봐요....

0

531

2

Vanilla JavaScript로 SPA 만드는 자료 혹은 선택 기준을 추천해주실 수 있으신가요?

1

500

1

특정 타이밍 스크롤 애니메이션 적용하기 섹션 수강중입니다.

0

464

2

[#svg, #이미지프레임과 텍스트 싱크] 스크롤 값에 움직이는 svg path, 이미지프레임과 텍스트 싱크 맞추는 것, 2가지 질문이 있습니다.

0

461

2

페이지가 처음 로딩 되었을 때 애니메이션 처리가 되지 않는 느낌입니다

0

444

1

섹션2 번째, opacity=0 되지 않고 잔상이 남습니다.

0

540

1

원래 쿼리셀렉터에서는 띄워쓰기 하면안되나요?

0

606

2

라이브러리 질문

1

421

2

translateY대신 애플에서 사용한 것 처럼 matrix로 scale의 크기를 주려고 하는데

0

416

1

[스크롤 높이 세팅] scrollHeight값이 3990아닌 4645로만 나오는데 뭐가 문제일까요? ㅠㅠ

0

607

2

scrollLoop 함수 질문

0

483

2

도메인 웹호스팅시 이미지가 안 뜨는데 누가 좀 알려주세요ㅠㅜ

0

1242

2

load 이벤트시 첫 비디오 이미지가 뜨네요.

0

513

2