월 15,400원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결애플 웹사이트 인터랙션 클론!
블렌드되는 이미지가 같이 스크롤되어 올라갑니다 ..
https://github.com/GyuryHan/apple_website 제가 봤을땐 오류가 없는 것 같은데 뭐가 문젠지 제대로 작동이 안됩니다 ㅜㅜ 코드 한번 봐주실 수 있을까요,,?
- 미해결애플 웹사이트 인터랙션 클론!
scroll-section 1에서 2로 넘어갈때 잠깐 텍스트들이 뜹니다
(() => { let yOffset = 0; // window.pageYOffset 대신 쓸 변수 let prevScrollHeight = 0; // 현재 스크롤 위치(yOffset)보다 이전에 위치한 스크롤 섹션들의 스크롤 높이값의 합 let currentScene = 0; // 현재 활성화된(눈 앞에 보고있는) 씬(scroll-section) let enterNewScene = false; // 새로운 scene이 시작된 순간 true const sceneInfo = [ { // 0 type: 'sticky', heightNum: 5, // 브라우저 높이의 5배로 scrollHeight 세팅 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, // type normal에서는 필요없음 scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-1') } }, { // 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'), canvasCaption: document.querySelector('.canvas-caption') }, values: { } } ]; 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.offsetHeight; } 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 / 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; const scrollHeight = sceneInfo[currentScene].scrollHeight; const scrollRatio = currentYOffset / scrollHeight; switch (currentScene) { case 0: // console.log('0 play'); 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 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() { enterNewScene = false; prevScrollHeight = 0; for (let i = 0; i < currentScene; i++) { 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(); }); // DOMContentLoaded : HTML구조만 로드되면 바로 실행, 실행타이밍 빠름 window.addEventListener('load', setLayout); window.addEventListener('resize', setLayout); })(); 보통스크롤영역이 있는 scroll-section1에서 2로 넘어갈때 순간적으로 텍스트들과 핀이 잠깐 떳다가 사라집니다. 이게 처음 새로고침했을때만 나타나고 한번 지나가고나면 안나타납니다. 그리고 이렇게 이 부분은 원래 이렇게 다음섹션으로 넘어갈때 침범하는게 맞는건가요?
- 미해결애플 웹사이트 인터랙션 클론!
opacity out 이 안됩니다. script 검토 부탁드립니다.
(() => { let yOffset = 0; // window.pageYOffset 대신 사용할 변수 / 편의상 변수를 만들어서 적용 let pervScrollHeight = 0; //현재 스크롤 위치(yOffset)보다 이전에 위치한 스크롤 섹션들의 스크롤 높이값의 합 let currentScene = 0; //현재 활성화된 (눈 앞에 보고있는) 씬(scroll-section) let enterNewScene = false; // 새로운 scene이 시작된 순간 true const sceneInfo = [ { // 0 type: "sticky", heightNum: 5, // 브라우저 높이의 5배로 scrollHeight 세팅하겠다는 의미. scrollHeight: 0, // 여기서 고정값으로 세팅할 수 있지만, 각 디바이스(화면) 사이즈 변경에도 대응해야해서 따로 함수 처리 사용 objs: { container: document.querySelector("#scroll-section-0"), messaageA: document.querySelector("#scroll-section-0 .main-message.a"), messaageB: document.querySelector("#scroll-section-0 .main-message.b"), messaageC: document.querySelector("#scroll-section-0 .main-message.c"), messaageD: document.querySelector("#scroll-section-0 .main-message.d"), }, values: { messaageA_opacity_in: [0, 1, { start: 0.1, end: 0.2 }], messaageB_opacity_in: [0, 1, { start: 0.3, end: 0.4 }], messaageB_opacity_out: [1, 0, { start: 0.25, end: 0.3 }], }, }, { // 1 type: "normal", heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-1"), }, }, { // 2 type: "sticky", heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-2"), }, }, { // 3 type: "sticky", heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-3"), }, }, ]; function setLayout() { // 각 스트롤 섹션의 높이 세팅 for (let i = 0; i < sceneInfo.length; i++) { sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; sceneInfo[ i ].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`; } // console.log(sceneInfo); 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 / 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 - pervScrollHeight; // const scrollRatio = yOffset / 현재씬의 scrollHeight; const scrollHeight = sceneInfo[currentScene].scrollHeight; const scrollRatio = (yOffset - pervScrollHeight) / scrollHeight; // console.log(currentScene); // 섹션 확인 switch (currentScene) { case 0: // console.log("o play"); const messaageA_opacity_in = calcValues( values.messaageA_opacity_in, currentYOffset ); const messaageA_opacity_out = calcValues( values.messaageA_opacity_out, currentYOffset ); if (scrollRatio <= 0.22) { // in objs.messaageA.style.opacity = messaageA_opacity_in; } else { // out objs.messaageA.style.opacity = messaageA_opacity_out; } console.log(messaageA_opacity_in); break; case 1: // console.log("1 play"); break; case 2: // console.log("2 play"); break; case 3: // console.log("3 play"); break; } } function scrollLoop() { enterNewScene = false; pervScrollHeight = 0; // 초기화 작업 for (let i = 0; i < currentScene; i++) { // pervScrollHeight = pervScrollHeight + sceneInfo[i].scrollHeight; pervScrollHeight += sceneInfo[i].scrollHeight; } if (yOffset > pervScrollHeight + sceneInfo[currentScene].scrollHeight) { enterNewScene = true; currentScene++; document.body.setAttribute("id", `show-scene-${currentScene}`); } if (yOffset < pervScrollHeight) { 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("DOMcontentLoaded", setLayout); // => HTML /DOM구조만 로딩되면 실행 window.addEventListener("load", setLayout); // => 웹 페이지에 있는 이미지/모든 리소스까지 모두 로드된 후에 실행 window.addEventListener("resize", setLayout); })();
- 미해결애플 웹사이트 인터랙션 클론!
자꾸 에러가 뜨는데 모르겠습니다ㅠㅠ
안녕하세요 선생님. 강의 너무 유용하고 재밌게 듣고 있습니다. 자꾸 오류가 뜨는데 무슨 문젠지 모르겠어서 너무 답답해서 질문 남깁니다.ㅠㅠ opacity가 정의되어 있지 않다그러고 Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas) 이런 오류가 뜨네요.. 분명 캔버스로 잘 정의한 것 같 은데요...ㅠㅠㅠㅠㅠ 분명 선생님을 다 따라 쳤는데도 어디서 문제가 생긴건지 모르겠습니다.. 도와주시면 감사하겠습니다.흑흑 (() => { let yOffset = 0; // window.pageYOffset 대신 쓸 변수 let prevScrollHeight = 0; // 현재 스크롤 위치 (yOffset) 보다 이전에 위치한 스크롤 섹션들의 스크롤 높이 합 let currentScene = 0; // 현재 활성화 된(눈 앞에 보고 있는) 씬 (scroll-section) let enterNewScene = false; // 새로운 씬이 시작 된 순간 true const sceneInfo = [ { //0 type: 'sticky', heightNum: 5, //브라우저 높이의 5배로 scrollHeight 세팅 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'), canvas: document.querySelector('#video-canvas-0'), context: document.querySelector('#video-canvas-0').getContext('2d'), videoImages: [], }, values: { videoImageCount: 300, imageSequence: [0, 299], canvas_opacity: [1, 0, { start: 0.8, end: 1 }], 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, // type normal에서는 필요없음 scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-1'), }, }, //2 { type: 'sticky', heightNum: 5, // 브라우저 높이의 5배로 scrollHeight 세팅 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'), canvas: document.querySelector('#video-canvas-1'), context: document.querySelector('#video-canvas-1').getContext('2d'), videoImages: [], }, values: { videoImageCount: 960, imageSequence: [0, 959], canvas_opacity_in: [0, 1, { start: 0, end: 0.1 }], canvas_opacity_out: [1, 0, { start: 0.95, end: 1 }], 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, // 브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-3'), canvasCaption: document.querySelector('.canvas-caption'), }, values: {}, }, ]; function setCanvasImages() { let imgElem; for (let i = 0; i < sceneInfo[0].values.videoImageCount; i++) { imgElem = new Image(); // imgElem = document.createElement('img'); imgElem.src = `./video/001/IMG_${6726 + i}.JPG`; sceneInfo[0].objs.videoImages.push(imgElem); } let imgElem2; for (let i = 0; i < sceneInfo[0].values.videoImageCount; i++) { imgElem2 = new Image(); // imgElem = document.createElement('img'); imgElem2.src = `./video/002/IMG_${7027 + i}.JPG`; sceneInfo[2].objs.videoImages.push(imgElem2); } } 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.offsetHeight; } 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}`); const heightRatio = window.innerHeight / 1000; //캔버스 원래 height = 1000 sceneInfo[0].objs.canvas.style.transform = `scale(${heightRatio}) translate(-50%,-50%)`; sceneInfo[2].objs.canvas.style.transform = `scale(${heightRatio}) translate(-50%,-50%)`; } function calcValues(values, currentYOffset) { let rv; // 현재 씬(스크롤섹션)에서 스크롤 된 범위를 0~1 사이의 비율로 구하기 const scrollHeight = sceneInfo[currentScene].scrollHeight; const 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; // 현재 씬에서 얼만큼 스크롤했는지를 확인 const scrollHeight = sceneInfo[currentScene].scrollHeight; const scrollRatio = currentYOffset / scrollHeight; switch (currentScene) { case 0: let sequence = Math.round( calcValues(values.imageSequence, currentYOffset) ); objs.context.drawImage(objs.videoImages[sequence], 0, 0); objs.canvas.style.opacity = calcValues( values.canvas_opacity, currentYOffset ); 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 2: let sequence2 = Math.round( calcValues(values.imageSequence, currentYOffset) ); objs.context.drawImage(objs.videoImages[sequence2], 0, 0); if (scrollRatio <= 0.5) { // in objs.context.style.opacity = calcValues( values.canvas_opacity_in, currentYOffset ); } else { // out objs.context.style.opacity = calcValues( values.canvas_opacity_out, currentYOffset ); } 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: break; } } function scrollLoop() { enterNewScene = false; prevScrollHeight = 0; for (let i = 0; i < currentScene; i++) { 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('DOMcontentLoaded', setLayout) // 이미지가 업로드 되기 전에 실행 window.addEventListener('scroll', () => { yOffset = window.pageYOffset; scrollLoop(); }); window.addEventListener('load', () => { setLayout(); sceneInfo[0].objs.context.drawImage(sceneInfo[0].objs.videoImages[0], 0, 0); }); window.addEventListener('resize', setLayout); setCanvasImages(); })();
- 미해결애플 웹사이트 인터랙션 클론!
섹션구분
섹션구분을 컨테이너클래스 안에서 안하고 밖에서하면 안되는건가용??
- 미해결애플 웹사이트 인터랙션 클론!
동영상 추출 프로그램
동영상 프레임 추출 프로그램은 어떤걸 사용하고 계세요?? 다른 동영상으로 만들어보고 싶습니다 ㅜㅜ
- 해결됨애플 웹사이트 인터랙션 클론!
18:00에서 absolute에서는 width를 넓혀줘야지 넓어진다는게 어떤뜻인가요??
18:00에서 absolute에서는 width를 넓혀줘야지 넓어진다는게 어떤뜻인가요??
- 해결됨애플 웹사이트 인터랙션 클론!
13:50 c인포 배열에 담아놓는다는게 어떤뜻인가요?
13:50 "c인포 배열에 담아놓는다"는게 어떤뜻인가요? 1. "c인포"를 제가 잘못알아들은건지.. 2. "c인포 배열에 담아놓는다"는게 어떤뜻인지.. ^^;;
- 미해결애플 웹사이트 인터랙션 클론!
.main-message.a 띄어쓰기 질문
항상 수고가 많으십니다. 덕분에 직장을 다니며 열심히 하고있습니다. 질문은 다음과 같습니다. .main-message.a을 적을때 .main-message .a 와 같이 .a을 한칸 띄우고 적으니 오류가 났었습니다. 왜 한칸 띄우지 말고 .main-message다음에 바로 .a를 해야 하나요? scroll-section-0하고는 한칸 띄우는데 안그렇길래 여쭤봅니다.
- 미해결애플 웹사이트 인터랙션 클론!
안녕하세요! reflow부분에 대해서 궁금한게 있습니다 ㅠㅠ
안녕하세요! 매번 강의 잘듣고있습니당!! 이런 강의 만들어주셔서 항상 감사해요 ㅠㅠ 이 강의와 직접적인 연관은 아닌데 ㅠㅠcss최적화 부분 공부하다가 reflow 를 최소화하는게 좋다는 걸 알았고.. height, padding 등이 reflow을 일으키는 요소들이라고 해요..근데 보다가 몇가지 궁금해져서요 ㅠ 찾아보곤 있는데 마땅히 명확한건 못찾겠어서 ㅠ혹시 선생님은 아실까 하여 여쭤봅니다 ㅠ 답변주심 정말 ㅠㅠ 좋겠어요 ㅠ 1. reflow는 height, padding 등이 변하면 계속 일어나는 거지요? 2. 그렇다면 px이 아닌, %, vw, vh, rem 등 브라우저 크기에 따라서 가변적으로 변화를 일으키는 단위들은 브라우저 화면이 리사이징 될때마다 리플로우가 일어나게 할까요?? 3. 그럼 반응형페이지 퍼블리싱할 때 저 위에 요소들을 많이 쓸텐데.. 그럼 항상 리플로우가 일어나게하는건지..ㅠㅠ 이건 어쩔 수 없고 최대한 리플로우를 최소화 하는 방안을 찾아서 해야하는지(left, top 대신 translate 쓰기 등..) 선생님의 생각이 궁금합니다!! 여쭤볼데가 없어서 흡.. 답변해주시면 정말정말 ㅠㅠ 감사합니다!!
- 미해결애플 웹사이트 인터랙션 클론!
css 안먹히는데 ..
이부분이 제 css 이고 이부분이 강사님 css 인데 왜 저는 stickey 가 왜 전부다 block 이 되는거죠?.....css 가 안먹힙니다.. 왜 저는
- 미해결애플 웹사이트 인터랙션 클론!
코드 구조에 관하여 질문드립니다.
javascript에 익숙하지 않아서 코드 구조에 관하여 질문드립니다. yOffset, prevScrollHeight, currentScene 같은 경우 arrow function 내부에 선언한 뒤에 다른 함수들에서 인자로 받지 않고 전역 변수처럼 가져다가 쓰고 있는데 javascript에서 이런 방식으로 많이 코드를 작성하나요? + ES6에서 class 개념도 추가되었다고 하는데 많이 사용하시는 편인가요?
- 미해결애플 웹사이트 인터랙션 클론!
여기까지 성공!
저도 제이쿼리로만 하다가 자바스크립트로 하니까 덜덜 떨리긴 하지만 비슷하내요! 제이쿼리로도 구현해볼려고 합니다! 너무 재밌고 강의가 너무너무너무 친절합니다. 너무 좋아요 감사드립니다. 제주도 개발자님!
- 해결됨애플 웹사이트 인터랙션 클론!
스크롤사이즈가 안줄어들어요 ㅠ
스크롤사이즈가 안줄어들어요 ㅠ 콘솔에 height사이즈는 출력잘되는데, 스크롤사이즈 줄이는문장을 입력하니까 안줄어드네용.. //타임라인만들기 (function(){ const sceneInfo = [ //스크롤섹션 객체 4개 //애니매이션효과 sticky, 없는애 normal { //0 type:'sticky', heightNum:5, //브라우저높이의5배로 scrollheight세팅 scrollHeight: 0, objs:{ container: document.querySelector('#scroll-section-0') } }, { //1 type:'normal', heightNum:5, scrollHeight: 0, objs:{ container: document.querySelector('#scroll-section-1') } }, { //2 type:'sticky', heightNum:5, scrollHeight: 0, objs:{ container: document.querySelector('#scroll-section-2') } }, { //3 type:'sticky', heightNum:5, scrollHeight: 0, objs:{ container: document.querySelector('#scroll-section-3') } } ]; function setLayout(){ //각스크롤섹션 높이세팅 for (let i=0; i< sceneInfo.length; i++){ sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; sceneInfo[i].objs.container.style.height = '${sceneInfo[i].scrollHeight}px'; } } window.addEventListener('resize',setLayout); setLayout(); })();//감싸고,함수바로호출됨
- 미해결애플 웹사이트 인터랙션 클론!
신 숫자 if문 넣어야하지 않아용?
if(yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight){ if(currentScene < sceneInfo.length-1){ currentScene++; } } 이런식으로 해야하지 않을까요? 맨밑으로 내리니까 커런트씬이 4가 되네요
- 해결됨애플 웹사이트 인터랙션 클론!
강의중에 질문이 있어 여쭤봅니다.
안녕하세요 ^^ 선택자를 BEM 방법론으로 사용하지 않으신다고하셨는데, .message.a 로 사용하는 것과 .message--link__move 이렇게 하나만 javascript selector로 사용하는 것과 성능차이는 없을까요? 내부적으로 .message, .a 이렇게 두번 찾지 않을까 싶어서 궁금해서 여쭤봐요 ^^ 좋은 강의 항상 감사드립니다.
- 미해결애플 웹사이트 인터랙션 클론!
쌤 근데 document.body.offsetWidth 할때요
body는 queryselector 으로 안가져와도 가능한 속성인가요?
- 해결됨애플 웹사이트 인터랙션 클론!
스크롤 고정 문제
선생님 강의를 따라서 코딩을 하다보니, 스크롤이 고정되는 문제가 발생했습니다. 현재 쓰고 있는 브라우저는 크롬입니다.이를 어떻게 해결할 수 있을까요~?<현재 강의는 페이지 내용 CSS 작성 1을 듣고 있습니다>css 코드는 아래와 같습니다~ @charset 'utf-8'; html { font-family: "Noto Sans KR", sans-serif; font-size: 14px; } body { overflow: hidden; color: rgb(29, 29, 31); letter-spacing: -0.05em; background: white; } p { line-height: 1.6; } a { color: rgb(29, 29, 31); text-decoration: none; } .global-nav { height: 44px; padding: 0 1rem; } .local-nav { height: 52px; border-bottom: 1px solid #ddd; padding: 0 1rem; } .global-nav-links, .local-nav-links { display: flex; align-items: center; max-width: 1000px; height: 100%; margin: 0 auto; } .global-nav-links { justify-content: space-between; } .local-nav-links .product-name { margin-right: auto; font-size: 1.4rem; font-weight: bold; } .local-nav-links a { font: size 0.8rem; } .local-nav-links a:not(.product-name) { margin-left: 2em; } .scroll-section { position: relative; padding-top: 50vh; } #scroll-section-0 h1 { font-size: 4rem; text-align: center; } .main-message { display: flex; align-items: center; justify-content: center; margin: 5px; height: 3em; font-size: 2.5rem; } .main-message p { font-weight: bold; text-align: center; line-height: 1.2; } .description { padding: 0 1rem; font-size: 1.2rem; color: #888; } .description strong { float: left; margin-right: 0.2em; font-size: 3rem; color: rgb(29, 29, 31); }
- 해결됨애플 웹사이트 인터랙션 클론!
scrollHeight 오류
디버깅 했을 때는 값이 잘 들어간게 확인되었는데 실행하면 콘솔에 Cannot read property 'scrollHeight' of undefined 에러가 발생하네요 ㅠㅠㅠ 뭐가 문제일까요? currentScene을 console.log 해보니 -1이 나오네요.. (() => { let yOffset = 0; // window.pageYOffset 대신 쓸 변수 let prevScrollHeight = 0; //현재 스크롤 위치(yOffset)보다 이전에 위치한 스크롤 섹션들의 스크롤 높이값의 합 let currentScene = 0; //현재 활성화된(눈 앞에 보고 있는) 씬(scroll-section) const sceneInfo = [ { //0 type: "sticky", heightNum: 5, //브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-0"), }, }, { //1 type: "normal", heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-1"), }, }, { //2 type: "sticky", heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-2"), }, }, { //3 type: "sticky", heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-3"), }, }, ]; function setLayout() { //각 스크롤 섹션의 높이 세팅 for (let i = 0; i < sceneInfo.length; i++) { sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; sceneInfo[ i ].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`; } } function scrollLoop() { prevScrollHeight = 0; for (let i = 0; i < sceneInfo.length; i++) { prevScrollHeight += sceneInfo[i].scrollHeight; } if (yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) { currentScene++; } if (yOffset < prevScrollHeight) { //if (currentScene === 0) return; //브라우저 바운스 효과로 인해 마이너스가 되는 것을 방지(모바일) currentScene--; } document.body.setAttribute("id", `show-scene-${currentScene}`); console.log(currentScene); } window.addEventListener("resize", setLayout); window.addEventListener("scroll", () => { yOffset = window.pageYOffset; scrollLoop(); }); setLayout(); })();
- 미해결애플 웹사이트 인터랙션 클론!
안녕하세요. 강의보다 질문이 있어서요~
scrollRatio 변수에 곱하기 650을 하면 스크롤이 0부터 650까지 값을 찾아내는데 650 + 250을 하면 처음값이 250부터 900까지로 나오는게 왜 그런걸까요? 처음값이 250이 되는게 이해가 안돼서요 ㅜ