월 15,400원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결애플 웹사이트 인터랙션 클론!
currntScene 대해 잘 모르겟습니다.
쌤 안녕하세요 질문이 있어서 여쭙니다. 다른부분은 몇번 들으니 이해가 잘 됐는데 currntScene때문에 멘붕이 왔습니다... if(yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) 여기서 대괄호안에 currntScene가 들어간게 잘 이해가 되지 않습니다. 혹시 대괄호안에 들어간 currntScene 를 객체화 시킨것인가요? 제가 너무 수준낮은 질문을 하고있는건 아닌지 .. 아니면 다른강의를 먼저 들어야할까요? 도움좀 부탁드립니다
- 해결됨애플 웹사이트 인터랙션 클론!
함수 선언 관련 질문입니다.
안녕하세요 좋은 강의 감사드립니다. 질문은, playAnimation() 함수를 적용하려고function playAnimation() {} 이렇게 적었는데 함수가 인식이 안되는 문제입니다. setLayout() 과 scrollLoop() 까지는 잘 인식(선언)이 되는데 왜 갑자기 playAnimation() 함수만 인식이 안되는걸까요..? 제가 뭘 건드린걸까요? 함수위에 마우스를 대보면void 라고 나옵니다. 급해서 질문을 모바일로 작성했는데 나중에 스크린샷까지 첨부해서 다시 올리겠습니다. 감사합니다!
- 미해결애플 웹사이트 인터랙션 클론!
Console 에러
콘솔창에 heightNum=5 줄이 Uncaught SyntaxError: Invalid shorthand property initializer 에러 뜨는데, 동영상3번 돌려봐도 잘 모르겠어요 ㅠ //즉시호출함수 (() => { //스크린 높이 const sceneInfo = [ { // 0 // 브라우저 높이의 5배로 scrollHeight 세팅 //디바이스마다 높이가 다르기 때문에 각 기계의 높이를 가지고 와서 x5를 함 type: 'sticky', heightNum = 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; i < sceneInfo.length; i++){ sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; sceneInfo[i].objs.container.style.height = `${scrollHeight[i].scrollHeight}`; } console.log(sceneInfo); } setLayout(); })();
- 미해결애플 웹사이트 인터랙션 클론!
리액트로도 강의를 학습할수있을까요??
리액트로 이런 페이지 를 제작해보려 하는데요 리액트로도 강의를 학습할수있을까요??
- 미해결애플 웹사이트 인터랙션 클론!
main.js 를 적용한 후에 show-scene-1에서 2로 넘아기질 않습니다..ㅠㅠ
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <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"> <link rel="stylesheet" href="default.css"> <link rel="stylesheet" href="01.css"> </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="#" class="local-nav-item">개요</a> <a href="#" class="local-nav-item">제품사양</a> <a href="#" class="local-nav-item">구입하기</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. Blanditiis optio quasi maxime quam dolore sequi, beatae non qui dolorum magni aspernatur. Temporibus id debitis accusantium non porro. Veritatis, magnam saepe. Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nobis, debitis vero aliquam facilis soluta, facere, quis magnam ex deleniti dicta reprehenderit ad necessitatibus veritatis quas repellendus nemo tempore molestiae dignissimos. </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. Assumenda natus totam molestias sequi possimus ducimus nisi id. Odit, mollitia consequuntur voluptatum eius doloremque, aperiam, porro tenetur officiis ex illum nisi! Lorem ipsum dolor sit amet consectetur, adipisicing elit. Dolorum ducimus nemo harum iusto consequatur, ipsam necessitatibus nulla, tempora nobis, nam sunt nostrum accusantium atque. Itaque quas veniam eum molestiae nesciunt! </p> </section> <footer class="footer"> 2020, 최태호 </footer> </div> <script src="js/01.js"></script> </body> </html> (()=> { 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를 세팅(디바이스의 높이의 5배를 하기 위해 설정) 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, 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") } } ]; 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.yOffsetHeight + 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 / 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){ rv = values[0]; } else if (currentYOffset > partScrollEnd) { rv = values[1]; } } else { rv = scrollRatio * (values[1] - values[0]) + values[0]; } console.log(rv) 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(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: // let messageA_opacity_in = calcValues(values.messageA_opacity, currentYOffset); // objs.messageA.style.opacity = messageA_opacity_in; 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() { //전체 스크롤(타임라인 합)의 누적을 막기 위해 0으로 설정 enterNewScene = false; prevScrollHeight = 0; for(let i=0; i <currentScene; i++){ prevScrollHeight = prevScrollHeight + sceneInfo[i].scrollHeight; } if (yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) { enterNewScene = true; currentScene++; document.body.setAttribute('id', `show-scene-${currentScene}`); } if (yOffset < prevScrollHeight) { enterNewScene = true; if (currentScene === 0) return; //브라우저 바운스 효과로 인해 마이너스가 되는 것을 방지(모바일) currentScene--; document.body.setAttribute('id', `show-scene-${currentScene}`); } if (enterNewScene) return; playAnimation(); } window.addEventListener("scroll", () =>{ yOffset= window.pageYOffset; scrollLoop(); }); window.addEventListener('load', setLayout); window.addEventListener('resize', setLayout); })();
- 미해결애플 웹사이트 인터랙션 클론!
안녕하세요
1분코딩님 안녕하세요~ awwwards에 소개되는 웹사이트들도 스크롤에 따라 이미지/글이 샥 나타나 스크롤에 비해 더 천천히/빠르게 위로 움직이는 애니메이션을 많이 쓰던데 보통 이 방식으로 구현 하는지 아니면 스크롤트리거, 스크롤매직 같은 라이브러리를 많이 쓰는지 궁금합니다~
- 미해결애플 웹사이트 인터랙션 클론!
스크롤섹션 관련
스크롤섹션이 이런식으로 활성화가 됩니다... css부분에서 글로벌네브랑 로컬네브 선생님이 수정하신대로 수정하였는데도 고쳐지지가 않습니다 왜그런걸까요?ㅠㅠ
- 미해결애플 웹사이트 인터랙션 클론!
body에 폰트 픽셀을 고정하는 이유가 궁금합니다.
안녕하세요!좋은 강의 업로드해주셔서 감사합니다:) 기본 폰트 사이즈에 대해 궁금한 점이 있어 질문 드려요~ 1. 폰트를 픽셀로 고정하면 접근성에 좋지 않다고 어디선가 읽었는데요~페이지의 font-size를 픽셀로 고정하는 사이트들이 많은 것 같습니다. 이 예제에서도 body의 font-size를 14px로 고정하였는데,body에 폰트 사이즈를 설정하지 않은채 rem을 사용하면다양한 브라우저에서 같은 화면을 보여줄 수 없으니 고정하는 편이 좋을까요? 2. 보통 몇 픽셀을 폰트 기본 사이즈로 사용하는지도 궁금합니다.16px로 알고 있었는데, 이 또한 사이트 컨셉에 따라 다른 건가요? 궁금합니닷~!
- 미해결애플 웹사이트 인터랙션 클론!
섹션3 블렌딩 애니메이션
섹션3에서 블랜딩 애니메이션을 총 4 parts로 나눴습니다. 질문1 : part 2(바다 사진이 올라가는 부분)에서 스크롤 다운할 때는 이미지가 잘 올라오는데, 반대로 스크롤 업 할 때는 이미지가 다시 내려가지 않습니다. 여러번 고민해보고 해결해보려 했는데 잘 되지 않아 질문 남깁니다. 코드 핵심적인 부분은 쌤과 같은데 조금 다르게 코딩 한 부분들도 있습니다. 아래 깃허브 첨부할게요. 강의 감사합니다(유튭채널도 잘 보고있어요) :) https://github.com/jayxwoo/pj__web-page__1-airmug
- 미해결애플 웹사이트 인터랙션 클론!
fixed 된 곳에서 scroll not working
안녕하세요 해당 강의를 거의 다 듣고, 혼자 해보고 있는데, sticky-elem에서 fixed로 준 영역에서 스크롤이 되지 않는 이슈가 발생했습니다. 아무리 해결하려고 해도 해결이 되지를 않네요,, 혹시 도움 받을 수 있을가요,, 영상 촬영은 했는데요,, 어떻게 올리는지 몰라서 못했습니다. 조금 더 상황을 설명하면, sticky-elem영역(fixed된 영역) 부분에서 스크롤이 되지 않는 버그가 일어납니다. 이런 저런 방법을 사용해도 잘안되네요 ㅜㅜ
- 미해결애플 웹사이트 인터랙션 클론!
강좌와는 관련없는 질문하나만 드리겠습니다.!
안녕하세요. 선생님 강의 정말 유익하게 잘듣고 있습니다. 저는 현재 개발자로 활동하고 있습니다만...새로운 기술이나, 실무에 쓰이지는 않지만 재밌는 기술들의 튜토리얼을 듣는것을 좋아합니다. 선생님께서 강의하시는 리스트 면면을 살펴보면 프론트엔드에 포커스가 되서 좀더 창의적인 표현을 가능하게 해주는 것들이 많아서 저는 도움을 많이 받았습니다. 후속강좌들은 어떤것이 계획중인지는 모르겠으나... siteinspire같은데서 볼수있는 굉장히 수준높은 표현을 가능하게 해주는 강좌가 있으면 좋겠습니다. https://barba.js.org/showcase/ 이런 류의 표현을 배워보고 싶은데, 도무지 감도 안오고, 이런류의 사이트는 어떻게 만든다는 소개나 튜토리얼이 전혀 없어서요. 아마도, 여기서 제시된 예들은 barba.js와 gsap라이브러리, html canvas테크닉이 결합되어있는거 같습니다만 어떻게 해야하는건지 전혀감이 오지않네요. 하나의 라이브러리를 선정해서 재밌는 테크닉과 아이디어를 프론트 엔드 엔지니어링으로 풀어내게 해주는 강좌가 있으면 좋을것같아서 한번 말씀드렸습니다. :) 감사합니다.
- 해결됨애플 웹사이트 인터랙션 클론!
캔버스 크기 관련 질문
안녕하세요. 질문이 있는데요 저는 캔버스 화면을 애플 공홈처럼 꽉 채우고 싶은데 innerHeight에 맞춰서 그런가 이런 식으로 나오네요(1920*1080 모니터 기준) outHeight나 그 외 다른 height 값들을 대신 넣으면 화면이 꽉 차는 대신 화면이 안 예쁘게 늘어납니다. f11하면 제가 원하는대로 1920*1080 풀사이즈 정확하게 나오고요. 어떻게 수정해야 할까요? const heightRatio = window.innerHeight / 1080; sceneInfo[1].objs.canvas.style.transform = `translate3d(-50%, -50%, 0) scale(${heightRatio})`;
- 미해결애플 웹사이트 인터랙션 클론!
scroll-section-0 이 부분만 따로 작업하려 합니다
scroll-section-0 에 있는 내용만 추출하여 작업하고 싶은데 결과는 이미지 동작이 안되어서 질문드립니다. 위와 같이 한 섹션들만 따로 추출하여 확인하고 싶을 때는 어떻게 해야할까요? 그리고 같은 섹션인 연속적으로 나오는 부분은 어떻게 작업하면 좋을지 질문드립니다!(*예 scoll-section-0 과 같은 섹션이 나오도록)
- 미해결애플 웹사이트 인터랙션 클론!
머리가 복잡해요..
calcValues() 함수 부분에서 조금 머리가 안따라주고있는데요.. // 현재 씬(스크롤섹션)에서 스크롤된 범위를 비율로 구하기 let scrollRatio = currentYOffset / sceneInfo[currentScene].scrollHeight; rv = scrollRatio * (values[1] - values[0]) + values[0]; 위의 코드부분이 이해가 잘안갑니다. 1. scrollRatio는 스크롤된 범위를 비율로 구한다고하는데. 현재 활성화된 씬 높이 / 현재 씬의 높이 이 공식이 원래 있는 수학 공식인가요? 스크롤 된 범위 <<< 이게 제가 보고있는 현재 화면을 말하는걸까요? 2. rv = scrollRatio * (values[1] - ..... ) 이 코드를 풀어보면 현재 활성화된 씬 * 내가 활성화 시킬 요소의 범위 + 활성화시킬 요소의 값 으로 풀어봤는데.. 맞나요? 이것또한 1번과 같이 원래 있는 수학공식인건가요? 간단한 수학이겠지만.. 저한테는 너무 어렵네요 ㅠㅠㅠ 조금 더 설명이 가능하실까요? 죄송합니다.ㅜㅜ
- 미해결애플 웹사이트 인터랙션 클론!
오!!!제가 찾고싶었던 부분입니다
전에 질문드렸었는데 이부분이였습니다! 이제 해결되니 뭔가 시원하게 뻥뚫린 기분이네요 ㅋㅋㅋㅋ 마지막까지 잘 듣도록 하겠습니다!
- 미해결애플 웹사이트 인터랙션 클론!
질문있습니다.
텍스트들이 opavity 0과 1로 변하고 20~0 ,0 ~-20 으로 가는 부분 진행중에 있어서 문제 사항이 있어서 질문드립니다. 맨처음 시작시 sticky-elem 의 값이 tranlateY(40%)가 되서 시작이 되고 0.1 에서 0.2 부분까지는 잘되지만 0.25에서 0.3 가는 부분에서 텍스트가 덜컹거립니다. 어느 부분이 문제인지 궁굼하니다. (()=>{ let yOffset = 0; // window.pageYOffset 대신 쓸 변수 let prevScrollHeight = 0; // 현재 스크롤 위치보다 이전에 위치한 스크롤 섹션들의 스크롤 높이의 합 let currentScene = 0; // 현재 활성화된( 눈 앞에 보고 있는 ) 씬 ( scroll-section) let enterNewScene = false; // 새로운 scene 이 시작되는 순간 true; const sceneInfo = [ { // scrollSection : 0 type : 'sticky', heightNum : 5, // 브라우저 높이의 5배로 scollHeight 세팅 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 }], messageA_translateY_in: [20, 0, { start: 0.1, end: 0.2 }], messageA_opacity_out: [1, 0, { start: 0.25, end: 0.3 }], messageA_translateY_out:[0, -20,{start:0.25, end: 0.3}], } }, { // scrollSection 번호 : 1 type : 'normal', heightNum : 5, // 브라우저 높이의 5배로 scollHeight 세팅 scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-1") } }, { // scrollSection 번호 : 2 type : 'sticky', heightNum : 5, // 브라우저 높이의 5배로 scollHeight 세팅 scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-2") } }, { // scrollSection 번호 : 3 type : 'sticky', heightNum : 5, // 브라우저 높이의 5배로 scollHeight 세팅 scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-3") } } ]; function setLayout() { // 각 스크롤 섹션의 높이 세팅 for(let i = 0; i < sceneInfo.length; i++){ sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`; } let yOffset = window.pageYOffset; let totalScrollHeigt = 0; for(let i =0; i < sceneInfo.length; i++){ totalScrollHeigt += sceneInfo[i].scrollHeight; if(totalScrollHeigt >= 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 <= partScrollStart){ rv = (currentYOffset - partScrollStart) / partScrollHeight * (values[1] - values[0]) + values[0]; } else if ( currentYOffset < partScrollStart){ rv = value[0]; } else if (currentYoffset > partScrollEnd){ rv= value[1] ; } } else { rv = scrollRatio * (values[1] - values[0]) + values[0]; } return rv; }; function 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 : const messageA_opacity_in = calcValues(values.messageA_opacity_in, currentYOffset); const messageA_opacity_out = calcValues(values.messageA_opacity_out, currentYOffset); const messageA_translateY_in = calcValues(values.messageA_translateY_in, currentYOffset); const messageA_translateY_out = calcValues(values.messageA_translateY_out, currentYOffset); if(scrollRatio <= 0.22){ // in objs.messageA.style.opacity = messageA_opacity_in; objs.messageA.style.transform = `translateY(${messageA_translateY_in}%)`; } else { // out objs.messageA.style.opacity = messageA_opacity_out; objs.messageA.style.transform = `translateY(${messageA_translateY_out}%)`; } case 1 : break; case 2 : break; case 3 : break; } }; function scrollLoop(){ prevScrollHeight = 0; enterNewScene = false; for(let i = 0; i< currentScene; i ++){ prevScrollHeight += sceneInfo[i].scrollHeight; } if(yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight){ enterNewScene = true; currentScene++; document.body.setAttribute('id', `show-scene-${currentScene}`); } if(yOffset < prevScrollHeight){ enterNewScene = true; if(currentScene===0) return; // 브라우저 바운스 모바일 currentScene--; document.body.setAttribute('id', `show-scene-${currentScene}`); } if(enterNewScene) return; playAnimation(); } window.addEventListener('scroll', ()=>{ yOffset = window.pageYOffset; scrollLoop(); }); window.addEventListener('resize',setLayout); window.addEventListener('load', setLayout); setLayout(); }) ();
- 해결됨애플 웹사이트 인터랙션 클론!
질문드립니다!!!
안녕하세요! setLayout();이 적용이 안되는 것같아서 질문올립니다. 작성한 코드 링크 올립니다! https://www.notion.so/chrisjj/858d6612b3464dfda539a501ea339586https://www.notion.so/chrisjj/858d6612b3464dfda539a501ea339586감사합니다!
- 미해결애플 웹사이트 인터랙션 클론!
선생님 질문이 있습니다.
/** *ECMA6 SCRIPT STUDY */ //const arr=[1,2,3]; //바람직한 패턴이 아닙니다. (()=>{ //const arr=[1,2,3]; //지역변수로 선언하고 코딩하는 것을 제안. const sceneInfo=[ { type:'sticky', //0 heightNum:5, //브라우저 높이의 5배로 scrollheight 세팅. scrollHeight:0, //문서길이에 대해서 섹션의 애니메이션 구간을 미리 설정합니다. //일단 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 }] } }, //각구간을 4개로 나눴습니다. { //1 type:'normal', heightNum:5, scrollHeight:0, objs:{ container: document.querySelector('#scroll-section-1') } }, { type:'sticky', //2 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', scrollHeight:0, heightNum:5, objs:{ container: document.querySelector('#scroll-section-3'), canvasCaption: document.querySelector('.canvas-caption') }, values:{ } } ]; let yOffset=0; let prevScrollHeight=0; //현재 스크롤위치보다 이전에 위치한 스크롤 섹션들의 스크롤 높이값의 합. let currentScene=0; //활성화된 씬 let enterNewScene=false; //새로운 scene이 시작되는 순간. function setLayout(){ for(let i=0; i<sceneInfo.length; i++){ if (sceneInfo[i].type==='sticky'){ sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * 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.style.offsetHeight; } sceneInfo[i].objs.container.style.height=`${sceneInfo[i].scrollHeight}px`; } //정확하게 한번더 선언. 재선언 가능의 LET yOffset=window.pageYOffset; //load이벤트와 연관지어야하는 이유. let totalScrollHeight=0; for(let i=0; i<sceneInfo.length; i++){ totalScrollHeight+=sceneInfo[i].scrollHeight; if(totalScrollHeight >= yOffset){ currentScene=i; break; /* *총 전체스크롤 높이가 현재 커서하고있는 페이지 스크롤 위치보다 크면, *break를 사용하여 for 문을 탈출합니다. *그리고 i값을 currentscene으로 세팅해줍니다. */ } } document.body.setAttribute('id',`show-scene-${currentScene}`); } function calcValues(values, currentYOffset){//스크롤 비율 계산 let rv; let scrollRatio=currentYOffset/sceneInfo[currentScene].scrollHeight; let scrollHeight=sceneInfo[currentScene].scrollHeight; //currentYOffset은 playanimation에서 한개의 씬을 넘길때마다 초기화되는 스크롤의 값. //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=(yOffset-prevScrollHeight) / scrollHeight; switch (currentScene){ case 0: //let messageA_opacity_0=values.messageA_opacity[0]; //let messageA_opacity_1=values.messageA_opacity[1]; if(scrollRatio<=0.22){ 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)}%)`; } 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: break; } } function scrollLoop(){ prevScrollHeight=0; enterNewScene=false; for(let i = 0; i<currentScene; i++){ prevScrollHeight+=sceneInfo[i].scrollHeight; } if (yOffset>prevScrollHeight+sceneInfo[currentScene].scrollHeight){ enterNewScene=true; currentScene++; } if (yOffset<prevScrollHeight){ enterNewScene=true; if(currentScene===0) return;//모바일기기의 위로 올릴경우 -가 될 때를 방지 currentScene--; } document.body.setAttribute('id',`show-scene-${currentScene}`); //"setAttribute 속성은 id 값을 두번재 인자값으로 줄 수 있다. 라는 의미" console.log(currentScene); if (enterNewScene) return ; playAnimation(); } window.addEventListener('resize',setLayout); window.addEventListener('load',setLayout); //각 스크롤 높이를 로드, 리사이즈 될 때 스크롤의 높이를 잡아줍니다. window.addEventListener('scroll',()=>{ yOffset=window.pageYOffset; scrollLoop(); }); setLayout(); })(); 지금까지 따라 작성한 내용들인데 body요소의 id가 0 , 1,2,3 이런식으로 바뀌어야하는데 0, 1 까지만 바뀌고 2-3으로는 바뀌지가 않아서 2씬에서 핀위의 글씨가 보이지 않습니다 어딘지 잘모르겠습니다...
- 미해결애플 웹사이트 인터랙션 클론!
쌤 근데 function (){} 로 쓰시는 이유가있을까요?
자바스크립트에 관련된 질문인데.. const 변수 = () => {} 이런식으로 에로우 펑션으로 하지않는 이유가 무엇인지 궁금합니당.
- 미해결애플 웹사이트 인터랙션 클론!
선생님 이부분 질문있습니다.
type:'sticky', //2 heightNum:5, scrollHeight:0, objs:{ container: document.getElementById('scroll-section-2') } }, { type:'sticky', //3 scrollHeight:0, heightNum:5, objs:{ //container: document.querySelector('#scroll-section-3') container: document.getElementById('scroll-section-3') } } ]; function setLayout(){ for(let i=0; i<sceneInfo.length; i++){ sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; sceneInfo[i].objs.container.style.height=sceneInfo[i].scrollHeight; //기본적으로 문자열을 설정해야하나 ${--- } << ---이부분에 변수이름을 적용 가능 } console.log(sceneInfo); } setLayout(); })(); 주석과는 관계 없이 getelementbyid 로 id객체를 가져와서 그 객체에 style.height로 값을 저런식으로 주게되면 왜 적용이 안되는건가요?