월 15,400원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결애플 웹사이트 인터랙션 클론!
깃허브 올리는 거에 관해서.
선생님 다름이 아니라 깃허브에 선생님 코드를 제가 직접 타이핑하고 주석을 달아서 깃허브에 출처를 남겨 업로드하는게 가능할까요? 단 선생님 소스코드에서 모든 내용을 달리 하고 올리는게 가능할까요 예를들어 사진같은거나 동영상등을 다 다른거로 대체해서말입니다.
- 해결됨애플 웹사이트 인터랙션 클론!
description max-width 줄때
description max-width 줄때 padding 으로 줘도 되지 않나요 ? 선생님께서 말씀하신거처럼 rem 이나 em 은 최상위 엘리먼트와 상대적인 크기를 쓰는 것이 좋다고 하셨는데. 메뉴 만들때처럼 padding을 양옆으로 10 rem정도 주면 되지 않나요? 굳이 1000px을 maxwidth의 값으로 줄 필요가 있나요?
- 미해결애플 웹사이트 인터랙션 클론!
강사님 안녕하세요 질문있습니다!
1. 리액트로 인터랙티브한거를 다룰때 코드가 많이 바뀌나요? 2. 리액트로 인터랙티브한거를 만드는 강의를 간단하게나마 출시(?)할 생각은 없으신지 궁금합니다. 리액트에서 직접적으로 dom을 건드리는 방식은 안티패턴이라고해서 선호하지않는데.. 제가 리액트에대한 이해도가 부족해서인지 좀 어렵더라구요ㅠㅠ 그래서 강사님은 어떤식으로 리액트를 다루는지 궁금해서요! 읽어주셔서 감사합니다.
- 미해결애플 웹사이트 인터랙션 클론!
강사님 질문있어서 문의드립니다!
첫번째 문제 발생현상 : 새로고침 했을 경우 간혈적으로 이런 에러 문구가 출력이 됩니다. 섹션0부분에 이미지파일은 눈으로 봤을땐 정상적으로 출력된 상황입니다. 에러문구는 하단에 붙히겠습니다. main.js:258 Uncaught TypeError: 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)' at playAnimation (main.js:258) at scrollLoop (main.js:364) at main.js:368 두번째 문제 발생현상 : 이것도 간혈적으로 출력되는 문제이며 main.js:352 Uncaught TypeError: Cannot read property 'scrollHeight' of undefined at scrollLoop (main.js:352) at main.js:368 이러 한 문구가 출력이 됩니다. 정말 죄송하지만 제 코드한번 봐주실수 있을까요?? (() => { 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'), canvas: document.querySelector('#video-canvas-0'), context: document.querySelector('#video-canvas-0').getContext('2d'), videoImages: [] }, values: { videoImageCount: 300, imageSequence: [0, 299], 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'), 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'), canvasCaption: document.querySelector('.canvas-caption') }, values: { } } ]; function setCanvasImages() { for (let i = 0; i < sceneInfo[0].values.videoImageCount; i++) { imgElem = new Image(); imgElem.src = `./video/001/IMG_${6726 + i}.JPG`; sceneInfo[0].objs.videoImages.push(imgElem); } } setCanvasImages() 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.content.offsetHeight + 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 / 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); 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) { if (currentScene === 0) return // 브라우저 바운스 효과로 인해 마이너스가 되는 것을 방지(모바일) enterNewScene = true; currentScene--; document.body.setAttribute('id', `show-scene-${currentScene}`); } if (enterNewScene) return; playAnimation(); } window.addEventListener('scroll', () => { yOffset = window.pageYOffset; scrollLoop(); }); //window.addEventListener('DOMContentLoaded', setLayout); window.addEventListener('load', setLayout); window.addEventListener('resize', setLayout); })();
- 미해결애플 웹사이트 인터랙션 클론!
canvas draw image 질문..
강사님 안녕하세요 canvas에 여러개의 이미지를 draw Image를 requestAnimationFram을 이용하여 계속 그려주는 상황에서 각각 이미지를 회전이라던가 다른 transform 효과들을 주고 싶은데 이미지를 context.rotate(radian) 하여 회전을 주었지만 애니메이션프레임 반복으로인해 이미지가깨지게보이거나 기대했던 대로 되지 않았습니다 . 제가 하고싶은것은 이미지가 rotate된 상태(고정) 에서 애니메이션을통해 그려지는 것입니다. 방법이 있을까요?
- 해결됨애플 웹사이트 인터랙션 클론!
이미지 시퀀스에서 이미지 로딩에 대한 질문
안녕하세요!! 강의 너무 잘 듣고 있습니다! 첫번째 이미지 시퀀스 캔버스 부분에서 궁금한게 생겼는데요 지금 이미지 시퀀스에 사용되는 이미지가 300장이 되는데, 스크롤 하면 이미지 로딩으로 뚝뚝 끊기는 느낌없이 부드럽게 300장이 전부 출력이 되고 있더라구요! sceneInfo[0].objs.videoImages에 배열로 이미지 객체를 넣어놓으면, 배열에 있는 300개의 이미지 객체가 window load 이벤트시 전부 한꺼번에 로딩이 되는건가요? 300개의 이미지가 어디서 한꺼번에 로딩이 되는건지 궁금합니다!!
- 미해결애플 웹사이트 인터랙션 클론!
이미지 스크롤과 캔버스를 통한 이미지 스크롤
안녕하세요. 강의를 듣다 궁금한 점이 있어 질문 올립니다. 단순 이미지 스크롤이랑 캔버스에 이미지를 그리며 스크롤하는 것이랑 어떤 차이가 있나 궁금합니다. 성능면에서 많이 차이가 나는건가요?
- 애플 웹사이트 인터랙션 클론!
질문드립니다!
삭제된 글입니다
- 해결됨애플 웹사이트 인터랙션 클론!
선생님
안녕하세요 선생님. 입문한지 3주가 지나 개념 공부 이후 클론코딩을 여러 차례 시도하고 있는 중입니다.1. 죄송하지만 혹시 BEM을 사용하지 않고 코딩을 하시는 데에는 어떤 이유가 있으신지 너무 궁금해서요! 입문 당시 여러 사이트에서 BEM 방식을 추천해주더라구요. 실무에서는 잘 사용하지 않는 작명법인가요?2. 마찬가지로 CSS 파일 분할법에 대한 노하우를 배우고 싶은데, 현재 클론코딩 폴더를 뒤져보면 main.css 하나로 스타일을 적용하고 계셔서 궁금해요!혹시 SPA를 제작하는 데 있어 CSS를 여러 파일로 구분하는 것은 큰 의미가 없는 행위일까요? JS의 경우 모듈화를 거치다 보니 분명하게 함수의 용도나 목적이 다르면 분할을 하고 있는 상태라 조금 혼동이 있네요 ㅠㅠ 예를 들면 - nav.css, scroll-section.css, footer.css 등과 같이요! 요소로 분할한다면 폴더는 components 등의 네이밍으로 분류하고 싶은데, 실무에서는 그렇게 하지 않는지 궁금해서 여쭤봅니다! (사실 하나의 html 파일에서만 사용하는데 분할이 큰 의미가 없나 싶기도 해서요ㅠㅠ)스택 공부도 중요하지만 미리 코딩 습관을 들이는 것이 더 중요하지 않을까 해서 여러모로 걱정이 많아 글이 길어졌습니다. 저는 보기 좋은 코딩을 하는 것 또한 목표라 많은 분들의 노하우도 참고하고 싶어서요 ㅎㅎ... 전에 Flex - Grid 블로그 글을 먼저 읽었었는데, 설명 정말 잘하시는 것 같아서 부럽습니다 ㅠㅠ
- 미해결애플 웹사이트 인터랙션 클론!
강사님 질문있습니다.
const calcValues = (values, currentYoffset) calcValues 함수에 values 값이 제대로 들어가지 않습니다. calcValues 함수 속에서 console.log(values, "작동") 을 기입해도 undefined라고 등장하고, 당연히 해당 함수를 돌릴때, 계속 아래와 같은 에러가 등장합니다. Uncaught TypeError: Cannot read property 'length' of undefined --- 여기서 등장하는 length는 if (values.length === 3) { 이부분이라고 생각해주시면 감사하겠습니다. 아래 복사한 제 코드문입니다. 무엇이 잘못일까요...? (() => { 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: null, 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_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_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_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") } }, { // 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") } } ]; ///////////////////////////////////////////////// const setLayout = () => { // 각 높이 세팅 for (let i = 0; i < sceneInfo.length; i++) { if (sceneInfo[i].type === "sticky") { sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * innerHeight; } else { sceneInfo[i].scrollHeight = sceneInfo[i].objs.container.style.offsetHeight; } sceneInfo[ i ].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`; } totalScrollHeight = 0; yOffset = window.pageYOffset; 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 calcValues = (values, currentYoffset) => { let rv; let scrollHeight = sceneInfo[currentScene].scrollHeight; let scrollRatio = currentYoffset / scrollHeight; console.log(currentYoffset); if (values.length === 3) { let partScrollStart = values[2].start * scrollHeight; let 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; }; const playAnimation = () => { const Values = sceneInfo[currentScene].values; const objs = sceneInfo[currentScene].objs; const currentYoffset = yOffset - prevScrollHeight; const scrollHeight = sceneInfo[currentScene].scrollHeight; const scrollRatio = currentYoffset / scrollHeight; switch (currentScene) { case 0: console.log("0번입니다.", 0); if (scrollRatio <= 0.24) { objs.messageA.style.opacity = calcValues( Values.messageA_Opacity_in, currentYoffset ); objs.messageA.style.transform = `translateY( ${calcValues( Values.messageA_TranslateY_in, currentYoffset )}% )`; } else { objs.messageA.style.opacity = calcValues( Values.messageA_Opacity_out, currentYoffset ); objs.messageA.style.transform = `translateY( ${calcValues( Values.messageA_TranslateY_out, currentYoffset )}%)`; } break; case 1: console.log("1번입니다.", 0); break; case 2: console.log("2번입니다.", 0); break; case 3: console.log("3번입니다.", 0); break; } }; const 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; console.log(yOffset); scrollLoop(); }); window.addEventListener("load", setLayout); window.addEventListener("resize", setLayout); })();
- 미해결애플 웹사이트 인터랙션 클론!
requestanimationframe 질문
안녕하세요 강사님 두가지 질문이 있는데요 첫번째로는 function loop{... requestanimationframe(loop) } 일때 loop이 계속 돌고있는데 이건 그러면 loop함수가 없어지지 않고 계속 생성되고 있는건가요?? 답변주시면 감사하겠습니다. 그리고 두번째로는 위의 addEventListener함수에서 rafId = requestAnimationFramp(loop) 을 쓰지 않고 그냥 loop을 쓰면 되지 않나요? rafId는 loop안에서만 사용되어도 상관없다고 생각이 돼서요. 답변주시면 감사하겠습니다.
- 미해결애플 웹사이트 인터랙션 클론!
Requestanimationframe 질문
제가 이부분을 재귀로 생각했는데 재귀가 아닌 다음 프레임 시작전에 함수를 예약거는 개념으로 이해하면 되나요? 재귀라면 cancleanimationframe를 갈 일이 없는거 같아서 의문이 들어 질문했습니다.
- 미해결애플 웹사이트 인터랙션 클론!
오류 나는 이유 알수 있을까요ㅠㅠ
잘 따라하고 있던중, ㅇ이런 오류나는데 해결방법을 모르겠습니다 ㅠㅠ 답변부탁드립니다.
- 미해결애플 웹사이트 인터랙션 클론!
스크롤 오류 질문..
안녕하세요 강사님 복습 공부중에 질문이 생겨서 남깁니다.! 개인프로젝트에 강의에서 배운 스크롤 블랜딩효과를 주고 만들고 있습니다. 1. 강의에선 clearRect()를 사용안해도 다시 그려지던데 저는 clearRect()를 사용안하면 그려진게 그대로 남더라구요 clearRect()를 안쓰고 다시 그려지는 방법이 있는건가요? 2. 스크롤을 천천히하면 원하던 효과가 적용되는데 스크롤을 빠르게 해버리면 이미지가 겹쳐지거나 레이아웃이 꺠져버립니다. 스크롤을 빠르게하더라도 안정감있게 적용하는 방법이 있을까요?
- 미해결애플 웹사이트 인터랙션 클론!
recalculatedInnerHeight 연산값 문제
canvasScaleRatio = wdithRatio; 를 이용할 경우 아래와 같은 그림이 됩니다. (좌측은 캔버스의 높이를 사용하고 우측은 코드를 똑같이 사용함.) 물론 예제를 똑같이 따라하여 스크롤을 내릴 경우에는 보이지 않고 넘어갈 수 있지만 응용을 하여 다른곳에서 사용 할 경우 결점이 될 수 있다고 생각합니다. `canvas.height 이용${objs.canvas.height}` `recalculated 이용${recalculatedInnerHeight} `재계산값 이용${window.innerHeight / heightRatio}` 위와같은 세게의 코드를 생각하여 로그로 작성해본 결과 일반적으로 켄버스를 사용한다면 해결될거라 생각하지만. 비율을 사용한다면 3번 코드를 이용하는 점이 좋을것 같습니다. 제가 생각하지 못한 이유거나 더 좋은 피드백이 있으면 달아주시면 감사하겠습니다.
- 미해결애플 웹사이트 인터랙션 클론!
강사님 질문있습니다.
main.add.js파일을 복붙해서 넣었는데 오류가 발생되어서 제 아무리 찾아봐도 어디서 오타인지를 못찾아서 도움을 요청하려고 글을 남깁니다. 오류는 콘솔로그에 Uncaught TypeError: Cannot read property 'style' of null at playAnimation (main.js:257) at scrollLoop (main.js:355) at main.js:361 라고 뜹니다. 제 스스로 찾아야하는데 도움부탁드립니다. 우선 제 js코드를 올려드리겠습니다. (() => { 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 } 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) { if (currentScene === 0) return // 브라우저 바운스 효과로 인해 마이너스가 되는 것을 방지(모바일) enterNewScene = true; currentScene--; document.body.setAttribute('id', `show-scene-${currentScene}`); } if (enterNewScene) return; playAnimation(); } window.addEventListener('scroll', () => { yOffset = window.pageYOffset; scrollLoop(); }); //window.addEventListener('DOMContentLoaded', setLayout); window.addEventListener('load', setLayout); window.addEventListener('resize', setLayout); })();
- 미해결애플 웹사이트 인터랙션 클론!
resize 질문
안녕하세요. resize 할때, 프론트엔드의 프레임워크를 사용할 경우, 직접적으로 DOM을 접근하는 것은 일종의 안티패턴이잖아요. 그러면 resize 이후 변경된 값을 컴포넌트에 props로 넘기게 될텐데, 이 과정에서 잦은 리렌더링이 발생하잖아요. 그런경우에는 어떻게 하는것이 퍼포먼스 측면에서 좋을까요? 계속 리사이즈 하는것이 아니니 크게 상관없는 것인지. 아니면 다른 방식으로 처리할 수 있는 것인지 잘 모르겠네요.
- 미해결애플 웹사이트 인터랙션 클론!
pageYOffset에 대한 질문
선생님, 넘 초보적인 질문일 수 있는데, 페이지를 새로고침하면 즉, load 이벤트가 일어나도 이전에 스크롤했던 pageYOffset값은 그대로 유지되는건가요?
- 미해결애플 웹사이트 인터랙션 클론!
chrome에선 잘 작동하던 코드가 safari에선 제대로 적용되지 않네요
새로고침 시에 setLayout() 함수에서 현재 위치에 맞게 scene 위치를 변화시켜주는 코드가 크롬에선 잘되고 사파리에선 안되길래 로그를 찍어보니 pageYOffset이 로드 이벤트시에 무조건 0으로 찍히네요 ㅠㅜ 크롬에선 정상적으로 잘 작동합니다. 어쩔 수 없는 문제인걸까요??
- 해결됨애플 웹사이트 인터랙션 클론!
비디오 인터렉션 부분부터..
삭제된 글입니다