월 15,400원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결애플 웹사이트 인터랙션 클론!
화면이 다 로드되지 않은 상태에서 스크롤을 내리면 오류창이 발생합니다
현재 28강을 수강하고 있습니다. 오류가 언제부터 발생했는지는 모르겠는데, 어디서 발생했는지 도무지 못찾겠습니다 ㅜㅜ 화면이 다 로드된 상태에서는 스크롤 창을 움직여도 오류코드가 뜨지 않는데, 화면이 아직 다 로드되지 않은 상태에서 스크롤을 움직이면 Uncaught TypeError: Cannot read properties of undefined (reading 'scrollHeight')라고 뜹니다 (() => { // 함수를 자동으로 호출하는 코드 // (function(){}) (); 동일 // 전역변수 사용을 최소화 하기 위해서 let yOffset = 0; //window.pageYOffset 대신에 사용할 변수 let prevScrollHeight = 0; //현재 스크롤 위치(yOffset)보다 이전에 위치한 scroll-section 들의 높이값의 합 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: { videoImagesCount: 300, imageSequence: [0, 299], canvas_opacity: [1, 0, { start: 0.9, 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 }] // translate의 %는 100%가 전체몸통 기준이다 //전체 scrollHeight을 1로 봤을 때, 0.1, 0.2, ...의 비율. 전체의 0.1의(10%) 구간을 0.3 ~ 0.4의 시점동안 실행한다 } }, { // 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'), canvas: document.querySelector('#video-canvas-1'), context: document.querySelector('#video-canvas-1').getContext('2d'), videoImages: [] }, values: { videoImagesCount: 960, imageSequence: [0, 959], canvas_opacity: [1, 0, { start: 0.9, end: 1 }], messageA_translateY_in: [20, 0, { start: 0.15, end: 0.2 }], messageB_translateY_in: [30, 0, { start: 0.5, end: 0.55 }], messageC_translateY_in: [30, 0, { start: 0.72, end: 0.77 }], messageA_opacity_in: [0, 1, { start: 0.15, end: 0.2 }], messageB_opacity_in: [0, 1, { start: 0.5, end: 0.55 }], messageC_opacity_in: [0, 1, { start: 0.72, end: 0.77 }], messageA_translateY_out: [0, -20, { start: 0.3, end: 0.35 }], messageB_translateY_out: [0, -20, { start: 0.58, end: 0.63 }], messageC_translateY_out: [0, -20, { start: 0.85, end: 0.9 }], messageA_opacity_out: [1, 0, { start: 0.3, end: 0.35 }], messageB_opacity_out: [1, 0, { start: 0.58, end: 0.63 }], messageC_opacity_out: [1, 0, { start: 0.85, end: 0.9 }], pinB_scaleY: [0.5, 1, { start: 0.5, end: 0.55 }], pinC_scaleY: [0.5, 1, { start: 0.72, end: 0.77 }], pinB_opacity_in: [0, 1, { start: 0.5, end: 0.55 }], pinC_opacity_in: [0, 1, { start: 0.72, end: 0.77 }], pinB_opacity_out: [1, 0, { start: 0.58, end: 0.63 }], pinC_opacity_out: [1, 0, { start: 0.85, end: 0.9 }] } }, { // 3 type: 'sticky', heightNum: 5, 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.videoImagesCount; i++){ imgElem = document.createElement('img'); imgElem.src = `../video/sample1/IMG_${6726 + i}.JPG`; sceneInfo[0].objs.videoImages.push(imgElem); } let imgElem2; for (let i =0; i < sceneInfo[2].values.videoImagesCount; i++){ imgElem2 = document.createElement('img'); imgElem2.src = `../video/sample2/IMG_${7027 + i}.JPG`; sceneInfo[2].objs.videoImages.push(imgElem2); } } setCanvasImages(); function setLayout() { // 각 스크롤 섹션의 높이 세팅 for (let i = 0; i < sceneInfo.length; i++){ if(sceneInfo[i].type === 'sticky') { sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; // 결과값을 scroll-section의 높이로 넣어줘야함 } 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; //처음 새로고침을 하고 아직 스크롤을 하지 않은 상태에서는 body에 show-scene-${currentScene}가 붙지않기 때문에 //load와 동시에 body에 show-scene-${currentScene}을 붙여주기 위한 코드 작업이 아래의 코드이다. //currentScene을 자동으로 세팅하는 코드 let totalScrollHeight = 0; for(let i = 0; i < sceneInfo.length; i++){ totalScrollHeight = totalScrollHeight + sceneInfo[i].scrollHeight; if(totalScrollHeight >= 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})`; //각 디바이스(노트북, 스마트폰, 패드 등) 크기에 맞게 스케일을 조정해주는 코드 // 기존 높이(1080) 대비 줄어들거나 늘어난 디바이스의 높이(window.innerHeight)에 맞춰 이미지의 스케일을 조정해준다 sceneInfo[2].objs.canvas.style.transform = `translate3d(-50%, -50%, 0) scale(${heightRatio})`; } function scrollLoop(){ //몇번 째 scroll-section 이 눈앞에서 스크롤 중인지 판별하기 위한 함수 //scroll-section-0 + scroll-section-1 높이의 합보다 현재 스크롤의 YOffset의 크기가 클시 scroll-section-2가 시작됐다고 생각할 수 있다. enterNewScene = false; prevScrollHeight = 0; for (let i = 0; i < currentScene; i++){ prevScrollHeight = prevScrollHeight + sceneInfo[i].scrollHeight; } if (yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight){ //여기에서 sceneInfo[currentScene]는 i와 동일한 역할을 해준다. 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; //씬이 바뀌는 순간 true일 경우 함수를 종료함 playAnimation(); } function calcValues(values, currentYOffset){ // 배열에 넣어놓은 values 값 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'); let sequence = Math.round(calcValues(values.imageSequence, currentYOffset)); objs.context.drawImage(objs.videoImages[sequence],0,0); //sequence에 해당 객체가 선택이 되고, x y값이 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: // console.log('2 play'); let sequence2 = Math.round(calcValues(values.imageSequence, currentYOffset)); objs.context.drawImage(objs.videoImages[sequence2],0,0); if (scrollRatio <= 0.25) { // 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.57) { // 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.83) { // 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; } } window.addEventListener('scroll',() => { yOffset = window.pageYOffset; scrollLoop(); }); window.addEventListener('load', function(){ setLayout(); sceneInfo[0].objs.context.drawImage(sceneInfo[0].objs.videoImages[0],0,0); }); // window.addEventListener('DOMContentLoaded', setLayout); 로 대체 할 수 있음. // DOMContentLoaded는 이미지 등은 제외하고 content 레이아웃만 로드되면 바로 실행됨. window.addEventListener('resize',setLayout); })();
- 미해결애플 웹사이트 인터랙션 클론!
image-blend-canvas를 html에 추가하면
image-blend-canvas를 html에 추가하고 개발자 도구에서 모바일 화면으로 볼 때,scroll-section0, 2, 3의 height가 16880px이 되면서 추가된 캔버스들이 제대로 구현 되지 않는데.... 뒤에 강의에서 조정이 되나요?? 아니면 저의 오류인가요...?첫 번째 쭉 따라 했을 때도 이 오류가 있어서.. 혼자 찾아보다가 복습 하는 차원에서 그냥 다 지우고 다시 처음부터 따라 했었는데... 두 번째에도 똑같은 오류가 생겨서요ㅠㅠ
- 해결됨애플 웹사이트 인터랙션 클론!
setLayout에서 height 값이 늘어나지 않아요ㅜㅜ
오타가 있는건지 뭘 잘못쓴건지 도통 모르겠습니다ㅜㅜ 왜 style이 안생기는지 모르겠어요ㅜㅜ (() => { /* (function() {} )(); 익명함수와 동일 화살표 함수 : (() => {})(); 전역변수 사용을 피하기 위해서 사용*/ const sceneInfo = [ { //0 type :'sticky', heightNum: 5, //브라우저 높이의 5배로 scroll height 세팅 scrollHeight: 0, objs: { container : document.querySelectorAll('#scroll-section-0') } }, { //1 type :'normal', heightNum: 5, scrollHeight: 0, objs: { container : document.querySelectorAll('#scroll-section-1') } }, { //2 type :'sticky', heightNum: 5, scrollHeight: 0, objs: { container : document.querySelectorAll('#scroll-section-2') } }, { //3 type :'sticky', heightNum: 5, scrollHeight: 0, objs: { container : document.querySelectorAll('#scroll-section-3') } } ]; function setLayout() { //각 스크롤 섹션의 높이 세팅 for(let i = 0; i < sceneInfo.length; i++) { //innerHeight = 브라우저 높이 ,전역객체 window는 생략가능하다 sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; sceneInfo[i].objs.container.style.height = '${sceneInfo[i].scrollHeight}px' ; } } function scrollLoop() { console.log(window.pageYOffset); } window.addEventListener('resize', setLayout); window.addEventListener('scroll', () => { scrollLoop(); }); setLayout(); //실행 }) (); 중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.
- 해결됨애플 웹사이트 인터랙션 클론!
if (scrollRatio <= 0.22) 로 이벤트 발생 시점을 컨트롤 하는 부분과 values 에 있는 start 와 end 값
if (scrollRatio <= 0.22) 로 이벤트 발생 시점을 컨트롤 하는 부분과 values 에 있는 start 와 end 로 처리하는 부분이 동일하지 않나요?단순히 calcValues 도 하지 않기 위해서 if (scrollRatio <= 0.22) 와 같은 조건문이 있는걸까요?
- 미해결애플 웹사이트 인터랙션 클론!
캔버스에 그려진 whiteRectWidth 와 실제로 같은 크기로 설정된 div의 크기가 다른 경우
안녀 안녕하세요. 위와같이 콘솔창을 보시면 whiteRectWidth가 202.5로 찍혀서 똑같은 크기를 가진 div를 위에 생성했더니 캡쳐와 같이 너비가 틀린 것을 확인했습니다. 실제로 캔버스에 그려질때 화면시작점부터가 아니라 좀 더 뒤에서 그려지는 건가요?? .test { background-color: black; width: 202.5px; height: 800px; align-self: flex-start; } 테스트한 div의 크기
- 미해결애플 웹사이트 인터랙션 클론!
이미지 크기 질문
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 안녕하세요 선생님혹시 강의에서 사용하는 이미지 크기는 1920 * 1080의 이미지인데 1920 * 1080 의 이미지보다 작거나 큰 이미지를 사용할려면 어느 부분을 고쳐줘야하나요? html 파일의 canvas를 1920 * 1000 으로 하고 이미지의 크기를 1920 * 1000으로 수정해서 저장하여야하나요..?
- 미해결애플 웹사이트 인터랙션 클론!
canvasScaleRatio관련 문의
안녕하세요! 항상 수업 잘 듣고 있습니다. canvasScaleRatio 계산 설명 중 궁금한 점이 있어 문의드립니다. height에 딱 맞춰 계산한다고 하셨는데, 만약 브라우저창의 가로 너비가 더 길고 세로 길이가 더 짧아서 그 안의 캔버스의 가로 너비가 더 짧고, 세로 길이가 더 긴상태라면(즉, 캔버스보다 브라우저 창이 납작한 경우) widthRatio 가 canvasScaleRatio가 되기때문에 해당 값으로 scale조절이 된다면 캔버스가 화면 너비에는 꽉차게 되지만 height또한 더 길어지게 되어 height값이 화면과는 동일하지 않게 됩니다(화면보다 캔버스 세로 길이가 더 길어서 넘어가게됨) 그런데 영상 설명에서는 계속 height값을 꽉 맞추기 위해서라고 설명하시는데 이같은 경우는 height값이 화면가 동일하지 않습니다. 결론: 브라우저 창이 홀쭉하든 납작하든 heightRatio로 계산하는게 맞지 않나요?? case 3: // 가로/세로 모두 꽉 차게 하기 위해 여기서 세팅(계산 필요) const widthRatio = window.innerWidth / objs.canvas.width; const heightRatio = window.innerHeight / objs.canvas.height; let canvasScaleRatio; if (widthRatio <= heightRatio) { // 캔보스보다 브라우저 창이 홀쭉한 경우 canvasScaleRatio = heightRatio; } else { // 캔버스보다 브라우저 창이 납작한 경우 canvasScaleRatio = widthRatio; } objs.canvas.style.transform = `scale(${canvasScaleRatio})`; // objs.context.drawImage(objs.images[0], 0, 0); // // 캔버스 사이즈에 맞춰 가정한 innerWidth와 innerHeight // const recalculatedInnerWidth = window.innerWidth / canvasScaleRatio; // const recalculatedInnerHeight = window.innerHeight / canvasScaleRatio; // console.log(recalculatedInnerWidth, recalculatedInnerHeight); // const whiteRectWidth = recalculatedInnerWidth * 0.15; break;
- 미해결애플 웹사이트 인터랙션 클론!
pageYOffset 관련 질문 및 이해
안녕하세요 강의중 이해가 잘 된게 맞는지 의문이 들어 질문드립니다. 1. pageYOffset 이 현재 스크롤 된 위치가 맞나요? 2. delayedYOffset = delayedYOffset + (pageYOffset - delayedYOffset) * 0.1 의 0.1 은 pageYOffset의 값을 1/10 한 건가요?.. pageYOffset은 실제 스크롤 된 위치라면 delayedYOffset 은 실제 스크롤된 위치의 1/10 된 위치가 맞는건가요? 이 부분이 이해가 잘 안가내요.
- 미해결애플 웹사이트 인터랙션 클론!
.sticky-elem 질문입니다.
.sticky-elem {position: fixed;} 을 하고 left:0, left:100 을 주나 위치가 변동이 되지 않습니다. 마찬가지로 right 값을 줘도 변하지 않습니다. 결론은 top:0 이 값만 있어도 가운데로 고정이 됩니다.위치가 변하지 않는데 굳이 left:0 을 주는 이유가 궁금합니다.
- 미해결애플 웹사이트 인터랙션 클론!
이미지가 안보입니다...ㅠ(도움이 필요합니다!! 꼭 봐주세요!)
안녕하세요 선생님 강의 잘 듣고 있습니다! 다름이 아니라 밑에 수강생분 처럼 저도 이미지가 안보이고 Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLCanvasElement or HTMLImageElement or HTMLVideoElement or ImageBitmap or OffscreenCanvas or SVGImageElement or VideoFrame)'. 이런 에러가 계속 뜨는데.. 어떻게 해야할지 모르겠습니다..ㅠㅠㅠ 분명 계속 이미지 경로도 확인하고 문제가 없는거같은데...대체 뭐가 문제인지 도움이 필요합니다..!! 너무 해결이 안되니까 꼭...해결하고싶습니다.. 잘되던 텍스트 애니메이션도 작동이 안됩니다. 보니까 playanimaion함수에서 sequence도 console.log에 찍으면 undefined로 찍히는데 도저히 뭐가 잘못 됐는지 모르겠습니다. 선생님이 주신 완성 코드를 대입해도 안뜨네요ㅠㅠㅠ (() => { let yOffset = 0; // window.pageYOffset대신 쓸 변수 let prevScrollHeight = 0; //현재 스크롤 위치(yOffset)보다 이전에 위치한 스크롤 섹션들의 높이값의 합 let currentScene = 0; //현재 활성화된(눈 앞에 보고있는) 씬(scroll-section) // sceneInfo는 애니메이션을 처리하는 정보를 담아놓는 변수 let enterNewScene = false;//새로운 scene이 시작되는 순간 true로 바뀜 const sceneInfo = [ { //0 type: 'sticky', heightNum:5,//브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight: 0, // 애니메이션을 조작할 오브젝트를 objs에 담음 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객체의 메서드인 getContext를 인자 2d를 넣어 호출해 그림을 그려주는 기초를 연다. context: document.querySelector('#video-canvas-0').getContext('2d'), //비디오 이미지를 배열에 넣을 예정이다. videoImages: [] }, //오브젝트를 어느시점에 보이고 안보이게 할지를 담을 곳 css제어 //시작값 :0 끝값 :1 values: { videoImageCount: 300, //스크롤에 따른 이미지 순서의 [초기값,최종값] messageA와 다르게 섹션의 부분에서 실행될 이미지가 아닌 한스크롤에서 쭉 이어지므로 start,end구간이 필요없다 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 }], //30%~ 40% messageC_opacity_in: [0, 1, { start: 0.5, end: 0.6 }], messageD_opacity_in: [0, 1, { start: 0.7, end: 0.8 }], // 글자를 20%에서 0으로 가는건데 %는 css에 나중에 작업할 예정 세번째 인수에는 타이밍이 들어간다(=어느 타이밍에 효과를 줄것인가) 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 }] // messageA_opacity:[200,900] } }, { //1 type: 'normal', //heightNum은 scrollHeight를 결정할 때 innerheight의 몇배로 할지 정해주는 수 였는데 normal은 원래 본인 default높이로 설정하므로 필요가 없다 // 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'), 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.5, end: 0.55 }], messageC_translateY_in: [30, 0, { start: 0.72, end: 0.77 }], messageA_opacity_in: [0, 1, { start: 0.15, end: 0.2 }], messageB_opacity_in: [0, 1, { start: 0.5, end: 0.55 }], messageC_opacity_in: [0, 1, { start: 0.72, end: 0.77 }], messageA_translateY_out: [0, -20, { start: 0.3, end: 0.35 }], messageB_translateY_out: [0, -20, { start: 0.58, end: 0.63 }], messageC_translateY_out: [0, -20, { start: 0.85, end: 0.9 }], messageA_opacity_out: [1, 0, { start: 0.3, end: 0.35 }], messageB_opacity_out: [1, 0, { start: 0.58, end: 0.63 }], messageC_opacity_out: [1, 0, { start: 0.85, end: 0.9 }], pinB_scaleY: [0.5, 1, { start: 0.5, end: 0.55 }], pinC_scaleY: [0.5, 1, { start: 0.72, end: 0.77 }], pinB_opacity_in: [0, 1, { start: 0.5, end: 0.55 }], pinC_opacity_in: [0, 1, { start: 0.72, end: 0.77 }], pinB_opacity_out: [1, 0, { start: 0.58, end: 0.63 }], pinC_opacity_out: [1, 0, { start: 0.85, end: 0.9 }] } }, { //3 type: 'sticky', heightNum:5, scrollHeight: 0, objs:{ container: document.querySelector('#scroll-section-3') } } ]; function setCanvasImages() { let imgElem; for (let i = 0; i < sceneInfo[0].values.videoImageCount; i++){ //이미지 객체가 만들어짐 new Image(); 대신에 document.createElement('img')랑 같은말이다. 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') { //container본연의 높이로 가져와서 그 크기만큼으로만 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}`); // console.log(sceneInfo); //초기화 할 때 실행되는 setLayout은 currentScene이 정해지지 않은 상태에서 currentScene을 구하는 것이고, // 스크롤 할 때마다 실행되는 scrollLoop는 현재 활성화된 currentScene 까지의 스크롤양(prevScrollHeight)을 기준으로 일정량의 스크롤이 지나갔을 때 currentScene을 +1 또는 -1 하는 것이랍니다. } function calcValues(values, currentYOffset) { //여기서 인자 currentYOffset은 현재 씬에서 얼마나 스크롤 됐는지 //현재 섹션에서 얼마나 스크롤 되었는지를 비율로 구한다 스클로의 비율을 0~1사이로 정한다. 비율을 구해서 그 값을 css값에 대입해준다. //yoffset변수는 전체 레이아웃에서 스크롤이 어디 위치해있는지 알려주는 변수이다. 현재 씬에서 스크롤이 얼마나됐는지는 알 수 없다. let rv; const scrollHeight = sceneInfo[currentScene].scrollHeight; //현재 씬(스크롤섹션)에서 스크롤된 범위를 비율로 구하기 ==>let scrollRation = 현재 씬에서 스크롤된 값 / 현재 씬 전체 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) { //스크롤섹션에서 스크롤이 start에 도달하지 못했을 때 rv = values[0]; //opacity가 0이라는 뜻 } else if (currentYOffset > partScrollEnd) { //스크롤섹션에서 스크롤이 end를 완전히 벗어났을 때 rv = values[1]; //opacity가 1이라는 뜻 } else { rv = scrollRatio * (values[1] - values[0]) + values[0]; } // rv = parseInt(scrollRatio * (values[1] - values[0]) + values[0]); return rv; } } //애니메이션 함수 function playAnimation() { // 모든 섹션에 애니메이션을 주는 것은 비효율적이므로 switch문을 이용해서 현재 보고있는 섹션의 요소에만 애니메이션을 준다 const objs = sceneInfo[currentScene].objs; const values = sceneInfo[currentScene].values; const currentYOffset = yOffset - prevScrollHeight; const scrollHeight = sceneInfo[currentScene].scrollHeight; const scrollRatio = currentYOffset / sceneInfo[currentScene].scrollHeight; switch (currentScene) { case 0: let sequence = calcValues(values.imageSequence, currentYOffset); objs.context.drawImage(objs.videoImages[sequence], 0, 0); console.log(sequence); 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.25) { // 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.57) { // 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.83) { // 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; } } // keyframe이란: 애니메이션이 진행중에 변화가 있는 지점을 keyframe이라고 한다. function scrollLoop() { enterNewScene = false; prevScrollHeight = 0;//누적을 막기 위해서 // 현재 보고잇는 section이 몇번째 section인지 판별하려고함 for (let i = 0; i < currentScene; i++){ prevScrollHeight = prevScrollHeight + sceneInfo[i].scrollHeight; } // console.log(prevScrollHeight); if (yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) { enterNewScene = true; currentScene++; document.body.setAttribute('id', `show-scene-${currentScene}`); // setLayout함수에서 이미 id값이 정해져있지만 스크롤에 변함에 따라 체크하는 방식으로 쓴다 } if (yOffset < prevScrollHeight) { if (currentScene === 0) return;//브라우저 바운스 효과로 인해 마이너스가 되는 것을 방지(모바일) enterNewScene = true; currentScene--; document.body.setAttribute('id', `show-scene-${currentScene}`); } // console.log(currentScene); // enterNewScene이 참이라는것은 씬이 바뀌는순간인 것이고 참일때 return, 즉 playAnimation함수를 한타임 실행하지 않는다는것으로 음수값이 무시되고 다시 playAnimation함수가 실행된다. if (enterNewScene) return; playAnimation(); } window.addEventListener('scroll', () => { yOffset = window.pageYOffset; scrollLoop(); }); window.addEventListener('load', setLayout); // window의 모든 문서(html, 이미지, 동영상..)가 완료되고 나서 // 레이아웃 잡는 setLayout함수를 실행한다 // window.addEventListener('DOMContentLoaded', setLayout); //반대로 window의 html구조들만 완료되면 반대로 이미지는 로드되지않아도 setlayout함수를 실행하는것 //인데 load보다는 빨리 setlayout을 실행한다. window.addEventListener('resize', setLayout); // 윈도우가 리사이즈 되었을 때 setLayout을 호출한다 })(); <!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>AirMug Pro</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="css/main.css"> <script defer src="js/main.js"></script> </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="#">개요</a> <a href="#">제품사양</a> <a href="#">구입하기</a> </div> </nav> <section class="scroll-section" id="scroll-section-0"> <h1>AirMug Pro</h1> <!-- p태그의 값들을 sticky, 즉 스티커처럼 고정해놓고 스트롤의 값에 따라 보여지도록 p태그를 감싸고 있는 div에 class를 준다. --> <div class="sticky-elem sticky-elem-canvas"> <canvas id ="video-canvas-0" width="1920px" height="1080px"></canvas> </div> <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. Quas minus praesentium incidunt tempora repellendus? Laudantium sed excepturi quo nemo quisquam totam, cumque nam sit incidunt, corrupti libero temporibus obcaecati enim facilis dolore itaque minima dolorem sequi explicabo. Modi alias in ullam labore, minima voluptates ipsa ex laborum deleniti tempora incidunt architecto iure aut, molestias ab! Fugiat aliquam veritatis ad voluptatibus, quaerat officia doloribus aliquid nulla repellat quidem rem magni perferendis labore similique reprehenderit aut. Cum quis omnis ex iusto natus iste nisi magnam, nesciunt fugit aspernatur ullam? Temporibus, dolores ipsa. Odit odio aliquam sunt error corporis ab facere, illum sequi! Explicabo nulla, ipsum necessitatibus laboriosam eligendi aspernatur quam nostrum. Debitis est ad, vel in corrupti voluptas laboriosam quo, reiciendis quos quas culpa, atque voluptate tempora tempore quam accusamus ipsa asperiores voluptatibus adipisci non vero aspernatur eum nesciunt quis. A cum, molestiae eaque repellat similique esse eligendi reprehenderit amet omnis minus quae voluptatum delectus dolores repudiandae? Distinctio aut aspernatur, iure nemo, eum soluta error necessitatibus minus voluptatum laborum rem dignissimos quae. Error ut recusandae ullam magnam quas molestiae repellat culpa. Ab nihil sint voluptatum in? Enim suscipit debitis earum culpa sint maxime hic, repudiandae nulla ad error voluptatem fugiat blanditiis odit! </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. Eveniet at fuga quae perspiciatis veniam impedit et, ratione est optio porro. Incidunt aperiam nemo voluptas odit quisquam harum in mollitia. Incidunt minima iusto in corporis, dolores velit. Autem, sit dolorum inventore a rerum distinctio vero illo magni possimus temporibus dolores neque adipisci, repudiandae repellat. Ducimus accusamus similique quas earum laborum. Autem tempora repellendus asperiores illum ex! Velit ea corporis odit? Ea, incidunt delectus. Sapiente rerum neque error deleniti quis, et, quibusdam, est autem voluptate rem voluptas. Ratione soluta similique harum nihil vel. Quas inventore perferendis iusto explicabo animi eos ratione obcaecati. </p> </section> <footer class="footer"> 2020, 1분코딩 </footer> </div> </body> </html>
- 미해결애플 웹사이트 인터랙션 클론!
공간을 넘어가요!
공간을 넘어가서 lorem 값이 나오는데 왜 이러는지 알 수 있을까요...?! padding 값이 문제일까요 ??? 문제는 없는 것 같은데 원래 넘어갈 수 밖에 없는 건지 궁금합니다.
- 미해결애플 웹사이트 인터랙션 클론!
모르겟습니다ㅠㅠ..
안녕하세요, 스크립트 에러가 나서ㅠ 어떤게 문제인지 모르겟습니다,, 아래 스크립트 소스 드리고, 에러가 나는건 현재 이거랑 같습니다. (() => { const sceneInfo = [ { // 0 type:'sticky', heightNum: 5, // 브라우저 높이의 5배로 스크롤하이트 세팅 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; 1 < sceneInfo.length; i++) { sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * innerHeight; sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`; } } window.addEventListener('resize', setLayout); setLayout(); })();
- 미해결애플 웹사이트 인터랙션 클론!
중요 메모
식은 죽은 원래 맛이 없다.. 1분코딩님은 죽을 원래 싫어하신다.. 메모..
- 미해결애플 웹사이트 인터랙션 클론!
setCanvasImages 내의 반복문 작성시 imgElem.src 관련 질문
안녕하세요 선생님. 좋은 수업 감사합니다. imgElem.src = `./video/001/IMG_${6726_i}.JPG`; 혹시 위의 setCanvasImages 내에서 이미지 개수를 30장 정도로 줄이고, 시작 번호를 0001로 변경하려고 합니다. 이렇게 되는 경우에, ${} 내의 번호 중 0이 모두 생략되어, 이미지 번호가 한자리수면 0을 3개를 붙이거나, 두자리수가 되면 00을 붙여야 하게 되는 현상이 있었습니다. 혹시 0의 자리에 맞게 자동으로 처리하는 데에 어떤 키워드로 찾아보면 좋을지, 혹은 방법이 있는지 궁금합니다.
- 미해결애플 웹사이트 인터랙션 클론!
ㅜㅜ11강부터 오류한거같아요
안녕하세요 제가 청각장애인데 못들리지만 강의 잘 따라하고 있는데 11강부터 자꾸 오류 나와서 고치는부분 열심히 찾아봐도 잘모르겠어요...자막없으셔서 무슨말인지 모르고 답답해요ㅠ.. 혹시 완성 되신파일 보내주실수있을까요?..
- 미해결애플 웹사이트 인터랙션 클론!
안녕하세요 일분코딩 수강생입니다.
안녕하세요 일분코딩 수강생입니다 다름이아니라 제가 운영하는 velog에 일분코딩 강의를 제가 필요한 부분만 정리해서 올릴려고 합니다. 이과정에서 수업에 사용된 코드를 사용하거나 이코드를 제가 이해하기 편하도록 변형시켜서 정리하려고 합니다 (모든 코드가아닌 일부분) 혹시 이런 부분에서 허락해주실수 있는지 궁금해서 문의드립니다
- 미해결애플 웹사이트 인터랙션 클론!
새로고침하면 캔버스에 계속 sticky 클래스가 붙어요
초기에 실행 할 때는 잘 되다가 중간에 새로고침을하면 처음부터 sticky 클래스가 붙네요ㅜ 소스 비교 해보아도 어디가 잘못된건지 못찾겠어요 엉엉
- 미해결애플 웹사이트 인터랙션 클론!
선생님께서 만드신 포트폴리오 사이트 질문이 있습니다.
강의와는 상관없는 질문을 해서 죄송합니다. 강의가 아닌 다른 연락 루트를 잘 모르겠어서... 인프런에 질문을 올려봅니다. 선생님께서 만드신 3d로 만들어진 포트폴리오 사이트요. 기능이 재미있어서 저도 만들어 보고 싶은데 선생님의 포트폴리오 사이트처럼 만드려면 어떤 기술(라이브러리? 기능?) 을 공부해야 할까요?
- 미해결애플 웹사이트 인터랙션 클론!
ㅋㅋㅋㅋ볼려고했던건아니고 디렉토리에 유저아이디가 제가 쓰는 아이디랑 비슷하네요 ㅋㅋㅋ
반갑네여
- 미해결애플 웹사이트 인터랙션 클론!
scrollHeight 값이 미묘하게 틀려요
안녕하세요 강의 잘 듣고 있습니다! 다름이 아니라 setLayout() 에서 sticky 타입이 아닌 normal 타입은 objs.container.offsetHeight 값을 그대로 scrollHeight 값으로 넣어주도록 했는데요 개발자도구에서 확인해보면 그렇게 입력된 height 값이 실제 컨테이너의 height 값과 10~20px씩 꼭 차이가 있더라고요... pc 버전에서 그 정도면 크게 티 나는 수준이 아니라 괜찮긴 한데 문제는... 반응형으로 넘어가면 10~20px이 아니라 700~1000px 까지도 차이가 나서... 보통 스크롤 영역과 sticky 영역 사이에 커다란 공백이 생깁니다... 이런 차이가 왜 생기는지 알면 잡고 넘어갈 수 있겠는데 왜 생기는 건지도 잘 모르겠네요 ㅜㅜ; 강의 진행 중에 따라했던 파일도 확인해보면 조금씩 차이가 나던데 왜 이런 현상이 생기는 걸까요...?