25%
57,750원
다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결애플 웹사이트 인터랙션 클론!
선생님 궁금한점이 생겨서 질문드립니다
우선 좋은 강의 만들어주셔서 감사합니다 제가 프로젝트에 적용하려고 우선 강의보면서 만든 사이트를 배포 먼저 해두었는데요, Video폴더의 이미지가 워낙 많아서 그런지 웹 페이지 로딩 시간이 정말 정말 오래걸립니다로딩 시간을 줄이기 위해서 할 수 있는 방법이 뭐가 있을까요?,,
- 미해결애플 웹사이트 인터랙션 클론!
질문이요!
수업에 관련없는 질문이지만 화면에 그림 그리는 툴 이름이 뭐예요?
- 미해결애플 웹사이트 인터랙션 클론!
1:37 질문이요.
Math.round() //올림 대신parseInt() //정수형을 메소드를 써도 무관한가요?
- 미해결애플 웹사이트 인터랙션 클론!
10:17 질문이요
function playAnimation(current){}function scrollLoop(){... playAnimation(currentScene);}인자값을 포함시켜서 커렌트값을 호출해도 괜찮을까요?
- 미해결애플 웹사이트 인터랙션 클론!
22:37 질문이요!
만약 return을 안주더라도 currentScene = 0 으로 고정적으로 주어도 괜찮을까요?
- 미해결애플 웹사이트 인터랙션 클론!
맥os만 가능한가요?
강의와 관련있는 질문을 남겨주세요.• 강의와 관련이 없는 질문은 지식공유자가 답변하지 않을 수 있습니다. (사적 상담, 컨설팅, 과제 풀이 등)• 질문을 남기기 전, 비슷한 내용을 질문한 수강생이 있는지 먼저 검색을 해주세요. (중복 질문을 자제해주세요.)• 서비스 운영 관련 질문은 인프런 우측 하단 ‘문의하기’를 이용해주세요. (영상 재생 문제, 사이트 버그, 강의 환불 등) 질문 전달에도 요령이 필요합니다.• 지식공유자가 질문을 좀 더 쉽게 확인할 수 있게 도와주세요.• 강의실 페이지(/lecture) 에서 '질문하기'를 이용해주시면 질문과 연관된 수업 영상 제목이 함께 등록됩니다.• 강의 대시보드에서 질문을 남길 경우, 관련 섹션 및 수업 제목을 기재해주세요. • 수업 특정 구간에 대한 질문은 꼭 영상 타임코드를 남겨주세요! 구체적인 질문일수록 명확한 답을 받을 수 있어요.• 질문 제목은 핵심 키워드를 포함해 간결하게 적어주세요.• 질문 내용은 자세하게 적어주시되, 지식공유자가 답변할 수 있도록 구체적으로 남겨주세요.• 정확한 질문 내용과 함께 코드를 적어주시거나, 캡쳐 이미지를 첨부하면 더욱 좋습니다. 기본적인 예의를 지켜주세요.• 정중한 의견 및 문의 제시, 감사 인사 등의 커뮤니케이션은 더 나은 강의를 위한 기틀이 됩니다. • 질문이 있을 때에는 강의를 만든 지식공유자에 대한 기본적인 예의를 꼭 지켜주세요. • 반말, 욕설, 과격한 표현 등 지식공유자를 불쾌하게 할 수 있는 내용은 스팸 처리 등 제재를 가할 수 있습니다. 이 강의 맥북만 가능한가요? 제가 윈도우10 쓰는데 예제 파일들 압축 풀었는데 파일 encoding을 못하네요
- 미해결애플 웹사이트 인터랙션 클론!
텍스트와 캔버스 사이 간격
강의를 따라하며 첫번째 캔버스까지 넣었는데 mid-message 텍스트와 캔버스 사이의 간격이 첨부한 사진처럼 좁혀져있는 상태로 나옵니다.. 자바스크립트에서 문제가 있는건가요 아니면 css에서 문제가 있는건가요?..
- 애플 웹사이트 인터랙션 클론!
.sticky-elem display: none;질문
삭제된 글입니다
- 미해결애플 웹사이트 인터랙션 클론!
18:10 강의 중에서 궁금한게 있습니다.
메뉴가 자치해서 닿는 순간 숫자가 미리 변해서 저는 메뉴가 자치하는 높이 만큼 뺄줄 알았는데 position:absoulte을 사용하셨는데 position:absoulte는 공간을 차지 하지 않는건가요??
- 미해결애플 웹사이트 인터랙션 클론!
고기를 잡는 법이 궁금합니다!
안녕하세요 선생님! 좋은 강의 감사히 잘 듣고있습니다 강의의 내용을 나름 차근차근 잘 따라가고, 선생님과 완전히 같은 페이지가 아닌, 저 나름의 동영상과 사진을 이용해서 저의 페이지를 만들며 강의를 수강하고 있습니다! 마침 선생님의 강의 내용에 해당 질문이 적합하여 질문드리고자 하는 것이 있습니다 선생님의 페이지와 마찬가지로, 제가 만든 페이지에서도 브라우저를 resize한 경우 scene3의 애니메이션이 딱 맞지 않게 작동한다던가, 모바일 환경에서 위에서 아래로는 정상적으로 스크롤 되지만 아래에서 위로 스크롤 하는 경우 애니메이션이 정상적으로 작동하지 않는다던가, 아이패드 환경에서 사진이 갑자기 사라졌다가 나타나는 등 수도 없이 많은(…ㅠㅠ) 오류가 발생하곤 하는데요, 선생님의 경우처럼 rectStartY가 초기화 되지 않아서 이런 오류가 발생하는구나, 라는 식의 사고 전개가 초심자라 그런지 잘 되지 않습니다. 코드를 올리고, 이러이런 현상이 발생하는데 어떻게 해결할까요 라는 질문도 좋은 질문이겠지만, 언젠가는 저 스스로 이런 문제를 해결하고자 하는 마음이 있는데, 어떤 방식으로 원인을 찾아 나가는 것이 좋은 접근일까요? 질문이 장황하여 죄송합니다. 정리하자면 원하는 틀을 만든 후에, 다양한 환경에서 테스트 할때, 어떤 문제가 발생하는지 까지는 파악 할 수 있으나, 그 문제가 왜 발생하였는지를 찾는데에 어려움을 겪고 있는데, 어떤 방식으로 접근해야 각 문제의 발생원인을 찾는데에 도움이 되는지 여쭤보고 싶습니다! 좋은 강의 감사합니다 ㅎㅎ
- 해결됨애플 웹사이트 인터랙션 클론!
스크롤 영역이 이상하게 잡히고 스크롤 길이도 이상해졌는데 어디서 오류가 난 건지 알고 싶습니다
잘 따라가고 있었는데 어디서부터인가 꼬여서 오류가 나네요... 선생님이 하신 소스코드 비교하면서 봤는데 제 눈에 오타나 오류가 안 보여서 질문 남깁니다 ㅠㅠ (()=>{ let yOffset = 0; //window.pageYOffset 대신 쓸 변수 let prevScrollHeight = 0; //현재 스크롤 위치 (yOffset)보다 이전에 위치한 스크롤 섹션들의 스크롤 높이의 합 let currentScene = 0; //현재 활성화된(눈 앞에 보고 있는) 씬(scroll-section) const sceneInfo = [ { //0 type: 'sticky', heightNum: 5, //브라우저 높이의 5배로 srollHeight 세팅 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:[0, 1] } }, { //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.lenght; i++) { sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; sceneInfo[i].objs.container.style.height =`${scrollHeight[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 playAnimation(){ switch (currentScene){ case 0: console.log('0 play'); break; case 1: console.log('1 play'); break; case 2: console.log('2 play'); break; case 3: console.log('3 play'); break; } } function scrollLoop(){ prevScrollHeight = 0; for(let i = 0; i < currentScene; i++) { prevScrollHeight += sceneInfo[i].scrollHeight; } if(yOffset < prevScrollHeight + sceneInfo[currentScene].scrollHeight) { currentScene++; document.body.setAttribute ('id', `show-scene-${currentScene}`); } if (yOffset > prevScrollHeight){ if (currentScene === 0) return; //브라우저 바운스 효과로 인해 마이너스가 되는 것을 방지(모바일) currentScene--; document.body.setAttribute ('id', `show-scene-${currentScene}`); } playAnimation(); } window.addEventListener('scroll', () => { yOffset = window.pageYOffset; scrollLoop(); }) //window.addEventListener('DOMContentLoaded', setLayout); window.addEventListener('load', setLayout); window.addEventListener('resize', setLayout); setLayout(); })(); //같은 말: (function())();
- 미해결애플 웹사이트 인터랙션 클론!
add.js파일 적용 후 pin 오류
안녕하세요 선생님 강의 잘 듣고 있습니다 ㅎㅎ 다름이 아니라 main_add.js 파일 적용 후 pin부분때문에 Uncaught TypeError: Cannot read property 'style' of null 오류가 뜹니다... css파일과 html파일도 찾아보고 있는데 혼자서 해결해 보려고 했지만 시간만가고 해결책이 없는거 같아 질문드립니다 ㅎㅎ 사진은 오류가 나는 제 페이지입니다 ㅠ js코드 (() => { //이거슨 즉시 호출 함수 즉, (function(){ }()); 익명함수를 한번싼고 호출한거랑 같음. 이렇게하는 이유는 전역변수를 피하려고 let yOffset = 0; // window.pageYoffset 대신 쓸 변수 let prevScrollHeight = 0; //현재 스크롤 위치(yOffeset)보다 이전에 위치한 스크롤 섹션들의 스크롤 높이값의 합 let currentScene = 0; //현재 활성화된 (눈 앞에 보고있는) 씬(scroll-section) let enterNewScene = false; //새로운 scene이 시작되는 순간 true const sceneInfo = [ { //0 type: 'sticky', heighNum: 5, //브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight: 0, objs:{ // html DOM 객체 요소들 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 }], //{}안의 값은 애니메이션이 재생되는 구간 소수점인 이유는 비율로 했기때문에, 전체 스크롤에서 10% ~ 20%만 애니메이션으로 쓰겠다 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', //type normal에서는 height를 기본 default로 잡기때문에 필요가 없다 //heighNum: 5, //브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight: 0, objs:{ container: document.querySelector('#scroll-section-1') } }, { //2 type: 'sticky', heighNum: 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') }, 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', heighNum: 5, //브라우저 높이의 5배로 scrollHeight 세팅 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].heighNum * window.innerHeight; sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`; // ``는 ''가 아니라 ~와 같이 있는것인데, 백틱이라고 한다 }else if(sceneInfo[i].type === 'normal'){ sceneInfo[i].scrollHeight = sceneInfo[i].objs.container.offsetHeight; } sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}`; } yOffset = window.pageYOffset; let totalScrollHeight = 0; for(let i =0; i < sceneInfo.length; i++){ totalScrollHeight += sceneInfo[i].scrollHeight; //각 씬에 scrollHeight를 더해준다 if(totalScrollHeight >= yOffset){ //totalScrollHeight에 들어가는 값이랑 현재 스크롤위치값이랑 비교해서 현재 스크롤위치가 total보다 커지면 현재 위치를 currentScene으로 세팅하고 for문을 빠져나옴 currentScene = i; break; } } document.body.setAttribute('id', `show-scene-${currentScene}`); } function calcValues(values, currentYOffset){ //여기서 values는 opacity 0, 1 넣은 그 배열 , offset은 현재씬에서 얼마나 스크롤 됐는지 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; // yOffset(전체에서 현재 스크롤값) / 현재 씬의 scrollHeight switch(currentScene){ case 0: if(scrollRatio <= 0.22){ // in objs.messageA.style.opacity = calcValues(values.messageA_opacity_in, currentYOffset); objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageA.style.opacity = calcValues(values.messageA_opacity_out, currentYOffset); objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.42) { // in objs.messageB.style.opacity = calcValues(values.messageB_opacity_in, currentYOffset); objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageB.style.opacity = calcValues(values.messageB_opacity_out, currentYOffset); objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.62) { // in objs.messageC.style.opacity = calcValues(values.messageC_opacity_in, currentYOffset); objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageC.style.opacity = calcValues(values.messageC_opacity_out, currentYOffset); objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.82) { // in objs.messageD.style.opacity = calcValues(values.messageD_opacity_in, currentYOffset); objs.messageD.style.transform = `translate3d(0, ${calcValues(values.messageD_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageD.style.opacity = calcValues(values.messageD_opacity_out, currentYOffset); objs.messageD.style.transform = `translate3d(0, ${calcValues(values.messageD_translateY_out, currentYOffset)}%, 0)`; } break; // case 1: // break; !!!case1은 2번째 섹션인데 그거슨 normal이라서 뺏음 case 2: 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)})`; } //translateY를 translate3d로 바꿨는데 translate3d(x값, y값, z값)이다. 이것을 쓴 이유는 브라우저 업데이트에 따라 달라질 수 있는데 //transform속성중 3d가 붙은 애들은 하드웨어 가속이 보장이 된다 즉, 퍼포먼스가 좋다. 그래서 애플에서도 3d이동이 아니더라도 3d를 쓴다 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}`); //currentScene을 바뀔때만 설정한것 } if(yOffset < prevScrollHeight){ enterNewScene = true; if(currentScene === 0) return; //브라우저 바운스 효과로 인해 마이너스가 되는 것을 방지 (주로 모바일에서) currentScene--; document.body.setAttribute('id', `show-scene-${currentScene}`); //currentScene을 바뀔대만 설정한것 } if(enterNewScene) return; playAnimation(); } window.addEventListener('resize', setLayout); window.addEventListener('scroll', () => { yOffset = window.pageYOffset; scrollLoop(); }); //window.addEventListener('DOMContentLoad', setLayout); //DOMContent와 load의 차이는 load는 웹페이지의 이미지, 리소스들이 싹다 로딩이 되고 나서 실행 window.addEventListener('load', setLayout); //DOMC는 html객체들, DOM구조만 로드가 끝나면 바로 실행한다 이미지 같은것들이 로딩 안되더라도.. window.addEventListener('resize', setLayout); })(); html코드 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AirMug Pro</title> <link rel="stylesheet" href="css/default.css"> <link rel="stylesheet" href="css/main.css"> <link rel="preconnect" href="https://fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;900&display=swap" rel="stylesheet"> </head> <body> <div class="container"> <nav class="global-nav"> <div class="global-nav-links"> <a class="global-nav-item" href="#">About</a> <a class="global-nav-item" href="#">Ideas</a> <a class="global-nav-item" href="#">Shop</a> <a class="global-nav-item" href="#">Contact</a> </div> </nav> <nav class="local-nav"> <div class="local-nav-links"> <a href="#" class="product-name">AirMug Pro</a> <a href="#" class="">개요</a> <a href="#" class="">제품사양</a> <a href="#" class="">구입하기</a> </div> </nav> <section class="scroll-section" id="scroll-section-0"> <h1>AirMug Pro</h1> <div class="sticky-elem main-message a"> <p>온전히 빠져들게 하는<br>최고급 세라믹</p> </div> <div class="sticky-elem main-message b"> <p>주변 맛을 느끼게 해주는<br>주변 맛 허용 모드</p> </div> <div class="sticky-elem main-message c"> <p>온종일 편안한<br>맞춤형 손잡이</p> </div> <div class="sticky-elem main-message d"> <p>새롭게 입가를<br>찾아온 매혹</p> </div> </section> <section class="scroll-section" id="scroll-section-1"> <p class="description"> <strong>보통 스트롤 영역</strong> Lorem ipsum dolor sit amet consectetur adipisicing elit. Officia, commodi! Perspiciatis ex sapiente nihil architecto, sequi non impedit odit eos at magni dolorum aliquid quis nemo autem illo provident ipsa, corrupti recusandae asperiores aspernatur dignissimos! Aut iste neque iusto quaerat architecto quam ullam suscipit culpa, accusantium nesciunt porro adipisci cum magnam corrupti quia. Commodi iste ipsum, molestiae animi velit soluta error exercitationem id natus, architecto alias dolorem porro quia repellendus! Voluptatibus animi enim natus, id laboriosam nihil ducimus odio vel officiis commodi facere exercitationem aut laudantium quos repellendus molestiae doloremque modi ullam beatae quae ratione architecto iure maxime quam? Obcaecati impedit laborum nam optio sit, temporibus harum, expedita nihil incidunt officia tempora, iusto odio reprehenderit? Asperiores, ipsum ullam voluptatibus, totam veritatis odit, tempora molestiae impedit animi error hic molestias? Totam corrupti fuga rem minima cumque accusamus dolore modi explicabo corporis eos dicta exercitationem enim eius eum dolores laboriosam fugit nobis, aspernatur id ipsum maiores labore architecto consequuntur consectetur! Quaerat nisi officiis autem quod in ut officia. Error doloremque consequuntur ullam ipsa doloribus beatae deleniti culpa, cupiditate, perspiciatis ea odio voluptates repudiandae numquam facilis quae, dolorem consequatur provident impedit accusamus nesciunt placeat magnam! Sapiente quod aperiam eaque tempore omnis autem tenetur eum dicta totam quibusdam aliquam, vero, unde doloribus sint exercitationem dolore dolorum illo! Deleniti, recusandae voluptatibus? Ducimus ex atque doloribus soluta? Rem porro velit praesentium nesciunt, alias veniam amet perspiciatis natus veritatis, nisi vel culpa repudiandae impedit! Animi molestiae ipsam natus! Tempora consequatur quod aut impedit, dolores odio asperiores in! Quidem quisquam necessitatibus illo dolorem nihil reiciendis eum repudiandae ratione facere. Enim nemo tenetur rem, eos, veniam, incidunt beatae sapiente inventore necessitatibus illum aliquid aliquam provident ipsam! Magni id inventore debitis at odio dicta architecto quos dolorem ea dolores, nulla numquam ipsam voluptates delectus similique obcaecati ducimus. Expedita, impedit? Dolorum corporis in officiis deserunt ipsa dolores, distinctio similique nihil placeat illo ut reiciendis, ea libero, earum nesciunt omnis ratione id? Nostrum optio fugit voluptate reprehenderit eos voluptatum quaerat, molestias itaque accusantium enim dolores distinctio odit vel esse cum eaque voluptas vitae asperiores provident doloremque ipsa fuga cumque! Commodi sint odio recusandae, temporibus accusantium quis magni accusamus porro ducimus nam numquam harum placeat eveniet? Voluptatum officia ad provident expedita, amet nam ratione praesentium possimus! Sint ipsum vitae ipsam hic, nisi earum minima dicta minus. Mollitia similique nulla exercitationem consequatur praesentium quibusdam maxime velit quo iusto, saepe deserunt distinctio voluptas itaque veniam asperiores voluptatem! Hic, ratione, laudantium, suscipit optio nemo laboriosam sint ullam impedit repudiandae cumque amet. Repellendus ut sint ipsa! Exercitationem, dolorum libero pariatur repudiandae iusto eum minima tenetur dolore placeat nulla! Ut deleniti veritatis voluptate, quia quidem incidunt numquam nulla, tenetur at optio nostrum temporibus beatae eveniet, facilis animi maxime rem doloremque expedita minus porro architecto modi sapiente quisquam. Ipsa illo unde numquam tempore ipsum at delectus! Quam alias beatae tempora atque, ratione incidunt accusamus placeat saepe voluptatem? Blanditiis, obcaecati, exercitationem odit consequuntur rerum facilis debitis quasi hic modi doloremque id, dolore doloribus illo atque mollitia. Tenetur nostrum harum ab? </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> <div class="pin"></div> <div class="sticky-elem desc-message c"> <p> 디자인 앤 퀄리티 오브 스웨덴,<br>메이드 인 차이나 </p> </div> <div class="pin"></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. Inventore sit expedita at quibusdam ducimus illo nostrum atque porro totam veniam, omnis quaerat alias? Facilis quod obcaecati harum deserunt eos quas, repudiandae et odit nisi assumenda officiis beatae necessitatibus odio, voluptatem at blanditiis veniam ducimus eum aliquid tempore? Commodi dolores ut eaque illum vel dolor ducimus dolore distinctio repudiandae quisquam expedita libero nisi tempore consequuntur minima, sed saepe. Totam mollitia eaque corporis obcaecati dicta, modi nihil impedit soluta aperiam consequatur, necessitatibus voluptate ad! Sequi, assumenda molestiae exercitationem at ut fugit sapiente saepe cupiditate perferendis nulla nisi nesciunt ullam accusamus mollitia numquam doloremque? Praesentium assumenda architecto quae cupiditate recusandae, velit, ex nemo debitis sapiente neque aliquam delectus? Quam expedita nesciunt perferendis, vero officia ratione velit? Necessitatibus animi nobis quos fuga natus dolorum voluptatum, aliquid nemo quaerat perferendis explicabo repudiandae, reprehenderit ex delectus adipisci perspiciatis? Eum animi praesentium tempora exercitationem blanditiis, officia vitae voluptate sed. Fugit, maiores cumque! Nam reiciendis impedit corporis eaque, numquam ipsa dignissimos ex fugit. Ullam, reprehenderit incidunt praesentium voluptas qui tenetur exercitationem, officiis reiciendis consequuntur impedit explicabo aspernatur inventore! Quis exercitationem vel et voluptatum, sapiente dolore debitis doloribus, quas blanditiis neque vitae laboriosam deleniti! Dolore impedit facere repellat praesentium? </p> </section> <footer class="footer"> 2021, Made by ZZANGZZONG </footer> </div> </body> <script src="js/main.js"></script> </html>
- 미해결애플 웹사이트 인터랙션 클론!
선생님 안녕하세요 코드 적용 후 에러가 발생하고 있습니다 ㅠㅠ
벌써 두시간 째 원인 파악 하고 있는데 도저히 찾을 수가 없네요 선생님 도와 주세요 breakpoint 찍어봐도 제가 실력이 부족해서 지금까지 잘 해 왔는데 갑자기 이러니까 너무 슬프네요 살려주세요! 코드 첨부해드리겠습니다. (() => { let yOffset = 0; // window.pageYOffset 대신 쓸 수 있음 let prevScrollHeight = 0; // 현재 스크롤 위치 yOffset 보다 이전에 위차한 스크롤 섹션들의 스크롤 높이값의 합 let currentScene = 0; // 현재 활성화된 씬 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, // 노멀에서는 필요 없음 scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-1'), content: document.querySelector('#scroll-section-1 .description') } }, { // 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') }, 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, // 브라우저 높이의 5배로 scrollHeight 세팅 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.offsetHeigh; } 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: if (scrollRatio <= 0.22) { //in objs.messageA.style.opacity = calcValues(values.messageA_opacity_in, currentYOffset); objs.messageA.style.transform = `translate3d(${calcValues(values.messageA_tranlateY_in, currentYOffset)}%)`; } else { //out objs.messageA.style.opacity = calcValues(values.messageA_opacity_out, currentYOffset); objs.messageA.style.transform = `translate3d(${calcValues(values.messageA_tranlateY_out, currentYOffset)}%)`; } // if (scrollRatio <= 0.42) { // // in // objs.messageB.style.opacity = calcValues(values.messageB_opacity_in, currentYOffset); // objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_in, currentYOffset)}%, 0)`; // } else { // // out // objs.messageB.style.opacity = calcValues(values.messageB_opacity_out, currentYOffset); // objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_out, currentYOffset)}%, 0)`; // } // if (scrollRatio <= 0.62) { // // in // objs.messageC.style.opacity = calcValues(values.messageC_opacity_in, currentYOffset); // objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_in, currentYOffset)}%, 0)`; // } else { // // out // objs.messageC.style.opacity = calcValues(values.messageC_opacity_out, currentYOffset); // objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_out, currentYOffset)}%, 0)`; // } // if (scrollRatio <= 0.82) { // // in // objs.messageD.style.opacity = calcValues(values.messageD_opacity_in, currentYOffset); // objs.messageD.style.transform = `translate3d(0, ${calcValues(values.messageD_translateY_in, currentYOffset)}%, 0)`; // } else { // // out // objs.messageD.style.opacity = calcValues(values.messageD_opacity_out, currentYOffset); // objs.messageD.style.transform = `translate3d(0, ${calcValues(values.messageD_translateY_out, currentYOffset)}%, 0)`; // } break; case 1: // console.log('1 play'); 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 (currentScene) return; playAnimation(); } window.addEventListener('scroll', () => { yOffset = window.pageYOffset; scrollLoop(); }); window.addEventListener('load', setLayout); window.addEventListener('resize', setLayout); })(); // HTML <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>AirMug Pro</title> <!-- RESET CSS / STYLE CSS --> <link rel="stylesheet" href="css/default.css"> <link rel="stylesheet" href="css/blank.css"> <!-- 구글 폰트 --> <link rel="preconnect" href="https://fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;900&display=swap" rel="stylesheet"> </head> <body> <div class="container"> <nav class="global-nav"> <div class="global-nav-links"> <a href="#none" class="global-nav-item">Rooms</a> <a href="#none" class="global-nav-item">Ideas</a> <a href="#none" class="global-nav-item">Stores</a> <a href="#none" class="global-nav-item">Contact</a> </div> </nav> <nav class="local-nav"> <div class="local-nav-links"> <a href="#none" class="product-name">AirMug Pro</a> <a href="#none">개요</a> <a href="#none">제품사양</a> <a href="#none">구입하기</a> </div> </nav> <section class="scroll-section" id="scroll-section-0"> <h1>AirMugPro</h1> <div class="sticky-elem main-message a"> <p>온전히 빠져들게 하는<br>최고급 세라믹</p> </div> <div class="sticky-elem main-message b"> <p>주변 맛을 느끼게 해주는<br> 주면 맛 허용 모드</p> </div> <div class="sticky-elem main-message c"> <p>온종일 편안한<br>맞춤형 손잡이</p> </div> <div class="sticky-elem main-message d"> <p>새롭게 입가를<br>찾아온 매혹</p> </div> </section> <section class="scroll-section" id="scroll-section-1"> <p class="description"> <strong>보통 스크롤 영역</strong> Lorem ipsum dolor, sit amet consectetur adipisicing elit. Necessitatibus expedita, aliquam odio iste quasi fugit doloribus vero ab quaerat sunt, corrupti voluptatum officiis vitae nemo? Nostrum, doloremque eveniet. Vero, dicta facere optio sapiente assumenda ad cupiditate magnam repellendus sunt itaque nostrum beatae saepe esse ab sed ipsam ipsum consequatur, modi nam, veniam aliquid voluptas. Corporis ipsum praesentium non commodi veritatis, ad nobis? Recusandae sunt neque, ad odio illo non tempore impedit, tenetur itaque ipsum voluptatem! Asperiores deleniti fuga laboriosam quasi beatae nisi mollitia, quos modi incidunt temporibus assumenda in earum fugit et cumque sunt explicabo sit dignissimos quam ex ea aliquid qui alias veniam. Expedita repudiandae exercitationem eum. Eius distinctio aut quisquam expedita consequuntur eligendi quos excepturi maxime veritatis esse, porro, aliquam quod officiis natus vitae autem accusantium in perferendis? Sequi, odit doloremque fugiat repellat temporibus animi facere inventore dolorum, iusto pariatur similique consequatur culpa neque eligendi ab sapiente, facilis eum nostrum hic aperiam ad accusantium. Porro a non minima accusantium fugit mollitia ipsum dolorem molestias quod repellendus qui quibusdam deleniti quia officia ea, vero deserunt accusamus tempore, iure molestiae animi praesentium consequuntur totam rerum? Ex quod optio impedit. Unde quis eligendi optio voluptates ipsam est pariatur obcaecati debitis maxime! </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> <div class="canvas-caption"> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit. Nostrum provident aspernatur veniam sed assumenda, repellendus, necessitatibus et ipsa laudantium dignissimos blanditiis expedita id debitis, pariatur rerum iure nisi magni repellat quaerat totam explicabo veritatis similique praesentium. Dignissimos dolores expedita totam pariatur quis quae similique labore voluptates delectus ipsa veniam facere, officiis repellendus ipsam ab molestiae voluptas modi facilis eaque excepturi voluptatem fugit sed sapiente. Unde esse nostrum eos. Laboriosam eligendi repellat commodi ad, dolore accusantium similique eum beatae aspernatur. Nihil ex officia, quae ullam magnam quasi ea saepe corporis molestias magni maxime ab repudiandae praesentium, numquam amet porro minima eveniet, asperiores voluptates quam nemo hic deserunt. Ipsa praesentium, omnis similique fugiat voluptates animi explicabo illum mollitia sed magni? Quisquam aperiam laborum nostrum consequuntur dolor quidem minima amet pariatur repellendus aut eius, modi accusamus quasi. Quaerat nisi dolores maiores, animi optio quidem obcaecati quisquam, rerum maxime corrupti natus vitae voluptatibus totam! Provident suscipit, adipisci sed nisi illum minus non eius. Nisi perferendis vitae accusamus ipsa voluptatum sint necessitatibus asperiores quae fuga exercitationem? Quos odio saepe optio labore, officia modi perferendis doloribus provident nisi ratione repellat architecto atque aliquam neque consequatur eaque cupiditate? Rerum beatae obcaecati necessitatibus ipsa accusamus dolor nobis saepe. </p> </div> </section> <footer> <div class="footer"> 2021 김기운 </div> </footer> </div> <script src="js/blank.js"></script> </body> </html>
- 해결됨애플 웹사이트 인터랙션 클론!
scene 넘어갈때 애니메이션 효과에 문제가 발생했습니다.
안녕하세요! 좋은 강의 감사드립니다! 바로 질문 드리겠습니다. 현재 겪고 있는 문제는 스크롤을 내려서 다음 스크롤 섹션으로 넘어갈 때 다음 스크롤 섹션에 속해 있는 .stiky-elem 요소들이 잠깐 '깜빡' 하면서 보였다가 사라지는 문제입니다. 해결하기 위해 이것저것 건드려본 결과 sceneInfo에서 HeightNum 속성이 다른 섹션보다 높게 설정된 섹션에서만 이런 문제가 발생한다는 것을 발견했습니다. (예를 들어 sceneInfo[2] 만 HeightNum 7을 주면 세번째 섹션으로 넘어갈 때 세번째 섹션에 있는 .sticky-elem들이 잠깐 깜빡하고 사라집니다. 그 다음은 애니메이션 정상 실행 ) 그런데 완성 파일의 경우, 특정 섹션의 HeightNum을 높게 설정해도 이런 문제가 전혀 발생하지 않아서 제가 뭔가 놓치고 있는게 있는 것 같은데요, 혹시 scrollLoop 함수에서 document.body.setAttribute('id', `show-scene-${currentScene}`) 이부분이 늦게 처리되고 있는게 아닐까 싶었지만 완성 파일과 비교했을 때 코드 자체는 동일하게 들어가 있습니다. (ㅠㅠ) calcValues 함수에서 currentYOffset이 value[2]의 start값보다 작을 경우 playAnimation 함수를 통해 value[0]의 값인 <opacity> 0을 출력하게 해놨는데도 무시하고 잠깐 나타났다가 사라지니까 제 선에서는 도저히 해결 방법을 못찾겠습니다. (ㅠㅠㅠ) 빨리 비디오 처리 부분으로 넘어가고 싶은데 섹션 넘어갈때마다 .sticky-elem들이 깜빡거려서 애니메이션이 이어지지 않는 느낌이라, 이 부분을 해결해야 비디오 처리 부분으로 넘어갈 수 있을 것 같아서 질문 드립니다. 감사합니다!!
- 미해결애플 웹사이트 인터랙션 클론!
비디오에 맞춰 텍스트가 나타나는 타이밍 질문입니다
안녕하세요 선생님 강의 잘 듣고있습니다!! 다름이 아니라 스크롤 섹션 2의 경우에는 비디오 타이밍에 맞춰 텍스트가 나오는데 그 타이밍 설정은 직접 콘솔로 scrollRatio 값을 확인해가면서 정하면 되는걸까요??
- 미해결애플 웹사이트 인터랙션 클론!
vue 적용법이 궁금합니다
js, html을 제가 만들고 있는 vue의 프로젝트에 적용하고 싶은데, 일단 main.js를 어디에 import해야할지 잘 모르겠습니다. 그래서 현재 js파일을 적용을 못하고 있습니니다. 또 html파일을 vue파일로 어떻게 변환해야할지 잘 모르겠습니다 ㅠㅠ 답변 기다리겠습니다 감사합니다
- 미해결애플 웹사이트 인터랙션 클론!
브라우저 바운스로 인한 버그 방지에 대한 질문이 있습니다!
안녕하세요 선생님, 인터랙션 강의 재밌게 듣고있는 학생입니다 제가 질문을 드리고자 하는 것은 다름이 아니고, 강의를 들으면서 선생님께서 코딩을 하시기 전에 개략적으로 이런 식으로 하면 되겠다라고 스스로 생각을 미리 해보는데, if (currentScene === 0) return; 강의 말미에 해당 코드를 작성하셨는데, if (yOffset < prevScrollHeight && yOffset > 0) 다음과 같은 방식으로 코드를 작성하는 것은 어떨까요? 다양한 방식이 존재하겠지만 선생님께서 해당 방식을 사용한 이유가 궁금합니다! 또한, 여러 방식중에 어떤 것을 선택할때, 저는 조금 더 효율적인? 브라우저에 부담이 되지 않는 방식을 선택하는 것이 좋은 방향이라고 생각하는데, 어떤 코드가 더 효율적인지 여부를 판단하는 방식을 모르겠습니다ㅠㅠ 개발자 도구를 통해 테스트를 한다거나 하는 방식이 있을 것 같은데 어떤 방식이 있을까요? 새해 복 많이 받으세요 강의 정말 잘 듣고있습니다!
- 미해결애플 웹사이트 인터랙션 클론!
스크롤 이벤트 시작하는 위치 질문입니다
안녕하세요 선생님 강의 잘 듣고 있습니다. 다름이 아니라 스크롤 이벤트를 시작하는 위치 구하는 부분에서 이벤트가 실행될 scroll-section의 offsetTop으로 구하는 것과 이벤트가 실행될 scroll-section 위에 존재하는 scroll-section들의 높이 합으로 구하는 것이 차이가 있는지 궁금합니다.
- 미해결애플 웹사이트 인터랙션 클론!
동영상 질문드립니다
이미지로 추출한 동영상을 찍으실때 흰색배경에 촬영하신건가요? 아니면 촬영후 배경을 제거하신건가요?
- 미해결애플 웹사이트 인터랙션 클론!
이미지가 안뜨고 에러가 나는데 어떡해야하나요,
Uncaught DOMException: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The HTMLImageElement provided is in the 'broken' state. 콘솔창에 이러한 에러가 뜨고 이미지가 뜨질 않아요.ㅜㅜ