월 15,400원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결애플 웹사이트 인터랙션 클론!
영상 올리는거 도와주세요 ^^
안녕하세요, 현재 "inflearn"이라는 웹사이트를 통해서 Javascript를 배우고 있습니다. 다름이 아니라 따라하라는대로 따라해봤는데 오류가 발생해서요 왜 오류가 발생하는지 알고 싶어서 연락드려봤습니다. 간단한 코드로 구성되어 있어 계속 재차 확인해도 에러가 무엇인지 잘 모르겠어서요. 제 에러가 무엇인지 선생님께서 확인해봐 주실 수 있으신가요? 도움이 좀 필요합니다 ^^ 좋은 하루 보내세요
- 미해결애플 웹사이트 인터랙션 클론!
requestanimationframe 의 브라우저 호환성
여러가지 실행을 하고 구글검색을 하다보니 requestanimationframe의 브라우저 호환성에 질문이 생겼습니다. 여러 유저들이 함수를 하나 더 만들어 사용하는데 이것이 꼭 필요한 코드일까요? window.requestAnimFrame = function(){ return ( window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(/* function */ callback){ window.setTimeout(callback, 1000 / 60); } ); }(); 사파리, 파이어폭스, 크롬에서 문제없이 잘 사용이 되어 이 코드가 필요한지 의문입니다. 강사님은 어떻게 생각하시나요?
- 미해결애플 웹사이트 인터랙션 클론!
자바스크립트 기초 공부 어떻게 해야할까요? 기초문턱에서 실전으로 넘기기가 어려워요 ㅠ
함수 적용하시는데 어디에 왜 이게 어떻게 달라붙는건지 이해를 못하겠어요 ㅠ
- 미해결애플 웹사이트 인터랙션 클론!
메시지가 스크롤 했을때 없어지지 않습니다.
안녕하세요. 스크롤 애니메이션 구현 3까지 따라갔는데 show-scene-0 의 메시지가 show-scene-1 로 스크롤 했을때 없어지지 않고 계속 되는데 여러번 강의들으면서 놓친 부분이 있나 체크해봤는데 못찾겠어서 혹시 알려주실수 있나 해서 질문드립니다. https://seon-o.github.io/apple-website/
- 미해결애플 웹사이트 인터랙션 클론!
drawImage 메서드에 대해서 질문 드려요
첫번째 이미지를 canvas 엘리먼트의 context에 drawImage를 사용해 그렸고 두번째 이미지도 동일한 context의 drawImage를 사용해 그렸는데 첫번째 이미지는 왜 그대로 유지가 되는 건가요??
- 미해결애플 웹사이트 인터랙션 클론!
이미지 블렌딩 에러
두번째 블렌딩되어 위로 올라가져야할 이미지가 찔끔 보이면서 위로 같이 따라 올라갑니다. 콘솔 에러는 이렇게 떠요 ㅠ
- 미해결애플 웹사이트 인터랙션 클론!
scroll-section-3에서 reload 했을 때 원래 자리를 찾지 못합니다.
안녕하세요. 코드를 재구성하면서 따라가고 있는데 마지막 scroll-section-3에서 문자가 보이는 화면에서 새로고침을 했을 때, pageYOffset이 제자리에 있지 않고 위로 이동하는 현상이 발생합니다. 디버깅을 한다고 했는데 도저히 모르겠어서 질문드립니다. 도움 주시면 감사하겠습니다. # index.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 rel="stylesheet" href="style.css"> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;900&display=swap" rel="stylesheet"> </head> <body class="before-load"> <div class="loading"> <svg class="loading-circle"> <circle cx="50%" cy="50%" r="25"></circle> </svg> </div> <nav class="global-nav"> <div class="global-nav-links"> <a href="#">Rooms</a> <a href="#">Ideas</a> <a href="#">Stores</a> <a href="#">Contact</a> </div> </nav> <nav class="local-nav"> <div class="local-nav-links"> <a href="#" class="product-name">AirMug Pro</a> <div class="local-nav-right"> <a href="#">Overview</a> <a href="#">Specification</a> <a href="#">Purchase</a> </div> </div> </nav> <div class="container"> <div class="scroll-section" id="scroll-section-0"> <div class="sticky-elem sticky-elem-canvas"> <canvas id="video-canvas-0" width="1920" height="1080"></canvas> </div> <h1>AirMug Pro</h1> <div class="sticky-elem main-message" id="main-message-a"> Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet, quisquam. </div> <div class="sticky-elem main-message" id="main-message-b"> Lorem ipsum dolor sit amet consectetur, adipisicing elit. Eaque, mollitia. </div> <div class="sticky-elem main-message" id="main-message-c"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate, dolorem. </div> <div class="sticky-elem main-message" id="main-message-d"> Lorem ipsum dolor sit, amet consectetur adipisicing elit. Obcaecati cumque, vitae iste aperiam tempora impedit. Voluptatem nobis nihil deleniti sint! </div> </div> <div class="scroll-section" id="scroll-section-1"> <p class="description"> <strong>AirMug Pro</strong> Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta commodi illo voluptates quae, reiciendis laborum quam beatae similique recusandae itaque. Exercitationem, aspernatur quidem, qui alias eveniet aliquid neque numquam labore voluptates, harum ipsum error quam magni voluptatibus atque repellat pariatur assumenda deleniti quaerat reprehenderit. Tempore quaerat soluta praesentium esse error id a quod voluptate minima asperiores maxime repudiandae nobis molestias ipsam unde sunt consectetur quidem modi tempora enim, quas voluptas ad. Amet eveniet quas inventore excepturi tempore nisi ullam sed iusto harum, voluptatum earum libero, dolorum omnis. Voluptatum, sit. Repellendus explicabo aut, modi dolorum nihil delectus nam minus mollitia quaerat. </p> </div> <div class="scroll-section" id="scroll-section-2"> <div class="sticky-elem sticky-elem-canvas"> <canvas id="video-canvas-1" width="1920" height="1080"></canvas> </div> <div class="sticky-elem desc-message" id="desc-message-a"> Lorem ipsum, dolor sit amet consectetur adipisicing elit. Ipsam, quidem unde? Delectus optio architecto consectetur accusantium fugiat odit animi doloremque. <div class="pin"></div> </div> <div class="sticky-elem desc-message" id="desc-message-b"> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos, consequuntur? Suscipit ut maiores voluptatem, pariatur in consequatur sapiente delectus iure odio ipsa soluta atque modi! <div class="pin"></div> </div> <div class="sticky-elem desc-message" id="desc-message-c"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Eum dolore ut sunt, inventore distinctio saepe sit! Iure fugit neque, ducimus enim rem odit maiores consectetur. <div class="pin"></div> </div> </div> <div class="scroll-section" id="scroll-section-3"> <p class="mid-message"> <strong>Retina Mug</strong><br> Lorem ipsum dolor sit amet consectetur adipisicing elit. Commodi, ratione. </p> <canvas class="image-blend-canvas" id="video-canvas-2" width="1920" height="1080"></canvas> <p class="canvas-caption"> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium unde molestiae aliquam sit, incidunt rerum aliquid sunt animi quasi provident sapiente obcaecati autem maiores ex libero minima tempore? Cumque eligendi recusandae sint tenetur ducimus iusto, excepturi ullam in veritatis maxime, libero dolores eveniet dolor odit reiciendis aliquam itaque? Distinctio, corporis dolorem. Delectus iure asperiores, quia veritatis modi sequi aperiam iusto nulla, et ea non nihil consectetur, eos nemo. Obcaecati perferendis ut assumenda distinctio est nam autem deleniti. Culpa beatae recusandae et incidunt iste dolorem error quia, fugit dicta in, tenetur dolore ad ab? Tenetur, eius. Mollitia beatae esse quibusdam nam doloremque fuga architecto rerum rem? Minus quae praesentium iusto rem maxime tempora hic distinctio, est non laborum ipsam cupiditate quis, quod totam at itaque perspiciatis repudiandae nesciunt vero officiis natus doloribus inventore unde? Dolores amet molestiae cupiditate aliquam ratione doloribus enim libero odio maiores atque inventore doloremque officia consequuntur ut, pariatur quis dolorum fugiat. Placeat dolore earum aperiam odit beatae perspiciatis, at provident pariatur recusandae neque facilis exercitationem officia unde consectetur quam assumenda. Ex vero quis fugit magnam, excepturi, voluptates debitis ipsum dolorem iste incidunt natus laboriosam dolores quibusdam. Accusantium numquam facere ullam quos totam nam aliquid magni delectus laudantium! </p> </div> </div> <footer class="footer">Copyright by Whi</footer> <script src="main.js"></script> </body> </html> # main.js class Website { constructor() { this.enterNewScene = false; this.acc = 0.1; this.delayedYOffset = 0; this.rafState = false; this.rafId; this.sceneInfo = [ { type: 'sticky', heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-0'), localNav: document.querySelector('.local-nav'), canvas: document.querySelector('#video-canvas-0'), context: document.querySelector('#video-canvas-0').getContext('2d'), messageA: document.querySelector('#main-message-a'), messageB: document.querySelector('#main-message-b'), messageC: document.querySelector('#main-message-c'), messageD: document.querySelector('#main-message-d'), videoImages: [], }, values: { messageA_opacity_in: [0, 1, {start: 0.1, end: 0.2}], messageB_opacity_in: [0, 1, {start: 0.3, end: 0.4}], messageC_opacity_in: [0, 1, {start: 0.5, end: 0.6}], messageD_opacity_in: [0, 1, {start: 0.7, end: 0.8}], messageA_opacity_out: [1, 0, {start: 0.25, end: 0.3}], messageB_opacity_out: [1, 0, {start: 0.45, end: 0.5}], messageC_opacity_out: [1, 0, {start: 0.65, end: 0.7}], messageD_opacity_out: [1, 0, {start: 0.85, end: 0.9}], canvas_opacity: [1, 0, {start: 0.9, end: 1.0}], canvas: [0, 299, {start: 0, end: 1}], }, }, { type: 'normal', scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-1'), } }, { type: 'sticky', heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-2'), canvas: document.querySelector('#video-canvas-1'), context: document.querySelector('#video-canvas-1').getContext('2d'), messageA: document.querySelector('#desc-message-a'), messageB: document.querySelector('#desc-message-b'), messageC: document.querySelector('#desc-message-c'), pinA: document.querySelector('#desc-message-a .pin'), pinB: document.querySelector('#desc-message-b .pin'), pinC: document.querySelector('#desc-message-c .pin'), videoImages: [], }, values: { 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_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}], messageA_translateY_in: [20, 0, {start: 0.25, end: 0.3}], messageB_translateY_in: [30, 0, {start: 0.6, end: 0.65}], messageC_translateY_in: [30, 0, {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}], pinA_scaleY: [0.5, 1, {start: 0.25, end: 0.3}], pinB_scaleY: [0.5, 1, {start: 0.6, end: 0.80}], pinC_scaleY: [0.5, 1, {start: 0.87, end: 0.92}], canvas_opacity: [1, 0, {start: 0.9, end: 1.0}], canvas: [0, 959, {start: 0, end: 1}], } }, { type: 'sticky', heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-3'), canvas: document.querySelector('#video-canvas-2'), context: document.querySelector('#video-canvas-2').getContext('2d'), images: [], }, values: { rect1X: [0, 0, {start: 0, end: 0}], rect2X: [0, 0, {start: 0, end: 0}], rectStartY: 0, blendHeight: [0, 0, {start: 0, end: 0}], canvas_scale: [0, 0, {start: 0, end: 0}], }, } ] this.prepareVideoImages(); this.addEventListener(); } setLayout() { for (let idx = 0; idx < this.sceneInfo.length; idx++) { const info = this.sceneInfo[idx]; if (info.type === 'sticky') { info.scrollHeight = info.heightNum * window.innerHeight; } else { info.scrollHeight = info.objs.container.offsetHeight; } info.objs.container.style.height = `${info.heightNum * window.innerHeight}px`; } let totalScrollHeight = 0; this.currentScene = 0; for (let idx = 0; idx < this.sceneInfo.length; idx++) { totalScrollHeight += this.sceneInfo[idx].scrollHeight; if (totalScrollHeight > this.yOffset) { this.currentScene = idx; break; } } document.body.setAttribute('id', `show-scene-${this.currentScene}`); const heightRatio = window.innerHeight / 1080; this.sceneInfo[0].objs.canvas.style.transform = `translate3d(-50%, -50%, 0) scale(${heightRatio})`; } scrollLoop() { if (this.enterNewScene) { return } this.prevScrollHeight = 0; for (let idx = 0; idx < this.currentScene; idx++) { this.prevScrollHeight += this.sceneInfo[idx].scrollHeight; } if (this.yOffset > this.prevScrollHeight + this.sceneInfo[this.currentScene].scrollHeight) { this.enterNewScene = true; this.currentScene += 1; // TODO: REFACTOR this.prevScrollHeight = 0; for (let idx = 0; idx < this.currentScene; idx++) { this.prevScrollHeight += this.sceneInfo[idx].scrollHeight; } } else if (this.yOffset < this.prevScrollHeight) { this.enterNewScene = true; this.currentScene -= 1; this.prevScrollHeight = 0; for (let idx = 0; idx < this.currentScene; idx++) { this.prevScrollHeight += this.sceneInfo[idx].scrollHeight; } } document.body.setAttribute('id', `show-scene-${this.currentScene}`); this.playAnimation(); this.enterNewScene = false; } playAnimation() { const sceneScrollHeight = this.yOffset - this.prevScrollHeight; const sceneScrollRatio = sceneScrollHeight / this.sceneInfo[this.currentScene].scrollHeight; const objs = this.sceneInfo[this.currentScene].objs; const values = this.sceneInfo[this.currentScene].values; let imgIdx; let canvasHeightRatio; let canvasWidthRatio; let canvasRescaleRatio; switch (this.currentScene) { case 0: imgIdx = Math.round(this.calcValues(values.canvas, sceneScrollRatio)); canvasHeightRatio = window.innerHeight / objs.canvas.height; canvasWidthRatio = window.innerWidth / objs.canvas.width; canvasRescaleRatio = canvasHeightRatio; if (canvasWidthRatio > canvasHeightRatio) { canvasRescaleRatio = canvasWidthRatio; } objs.canvas.style.transform = `translate3d(-50%, -50%, 0) scale(${canvasRescaleRatio})`; // objs.context.drawImage(objs.videoImages[imgIdx], 0, 0); if (sceneScrollRatio < 0.22) { objs.messageA.style.opacity = this.calcValues(values.messageA_opacity_in, sceneScrollRatio); } else { objs.messageA.style.opacity = this.calcValues(values.messageA_opacity_out, sceneScrollRatio); } if (sceneScrollRatio < 0.42) { objs.messageB.style.opacity = this.calcValues(values.messageB_opacity_in, sceneScrollRatio); } else { objs.messageB.style.opacity = this.calcValues(values.messageB_opacity_out, sceneScrollRatio); } if (sceneScrollRatio < 0.62) { objs.messageC.style.opacity = this.calcValues(values.messageC_opacity_in, sceneScrollRatio); } else { objs.messageC.style.opacity = this.calcValues(values.messageC_opacity_out, sceneScrollRatio); } if (sceneScrollRatio < 0.82) { objs.messageD.style.opacity = this.calcValues(values.messageD_opacity_in, sceneScrollRatio); } else { objs.messageD.style.opacity = this.calcValues(values.messageD_opacity_out, sceneScrollRatio); } if (sceneScrollRatio > 0.85) { objs.canvas.style.opacity = this.calcValues(values.canvas_opacity, sceneScrollRatio); } break; case 1: break; case 2: imgIdx = Math.round(this.calcValues(values.canvas, sceneScrollRatio)); canvasHeightRatio = window.innerHeight / objs.canvas.height; canvasWidthRatio = window.innerWidth / objs.canvas.width; canvasRescaleRatio = canvasHeightRatio; if (canvasWidthRatio > canvasHeightRatio) { canvasRescaleRatio = canvasWidthRatio; } objs.canvas.style.transform = `translate3d(-50%, -50%, 0) scale(${canvasRescaleRatio})`; // objs.context.drawImage(objs.videoImages[imgIdx], 0, 0); if (sceneScrollRatio < 0.35) { objs.messageA.style.opacity = this.calcValues(values.messageA_opacity_in, sceneScrollRatio); objs.messageA.style.transform = `translate3d(0, ${this.calcValues(values.messageA_translateY_in, sceneScrollRatio)}%, 0)`; objs.pinA.style.transform = `scaleY(${this.calcValues(values.pinA_scaleY, sceneScrollRatio)})`; } else { objs.messageA.style.opacity = this.calcValues(values.messageA_opacity_out, sceneScrollRatio); objs.messageA.style.transform = `translate3d(0, ${this.calcValues(values.messageA_translateY_out, sceneScrollRatio)}%, 0)`; objs.pinA.style.transform = `scaleY(${this.calcValues(values.pinA_scaleY, sceneScrollRatio)})`; } if (sceneScrollRatio < 0.67) { objs.messageB.style.opacity = this.calcValues(values.messageB_opacity_in, sceneScrollRatio); objs.messageB.style.transform = `translate3d(0, ${this.calcValues(values.messageB_translateY_in, sceneScrollRatio)}%, 0)`; objs.pinB.style.transform = `scaleY(${this.calcValues(values.pinB_scaleY, sceneScrollRatio)})`; } else { objs.messageB.style.opacity = this.calcValues(values.messageB_opacity_out, sceneScrollRatio); objs.messageB.style.transform = `translate3d(0, ${this.calcValues(values.messageB_translateY_out, sceneScrollRatio)}%, 0)`; objs.pinB.style.transform = `scaleY(${this.calcValues(values.pinB_scaleY, sceneScrollRatio)})`; } if (sceneScrollRatio < 0.94) { objs.messageC.style.opacity = this.calcValues(values.messageC_opacity_in, sceneScrollRatio); objs.messageC.style.transform = `translate3d(0, ${this.calcValues(values.messageC_translateY_in, sceneScrollRatio)}%, 0)`; objs.pinC.style.transform = `scaleY(${this.calcValues(values.pinC_scaleY, sceneScrollRatio)})`; } else { objs.messageC.style.opacity = this.calcValues(values.messageC_opacity_out, sceneScrollRatio); objs.messageC.style.transform = `translate3d(0, ${this.calcValues(values.messageC_translateY_out, sceneScrollRatio)}%, 0)`; objs.pinC.style.transform = `scaleY(${this.calcValues(values.pinC_scaleY, sceneScrollRatio)})`; } if (sceneScrollRatio > 0.85) { objs.canvas.style.opacity = this.calcValues(values.canvas_opacity, sceneScrollRatio); } break; case 3: canvasHeightRatio = window.innerHeight / objs.canvas.height; canvasWidthRatio = window.innerWidth / objs.canvas.width; canvasRescaleRatio = canvasHeightRatio; if (canvasWidthRatio > canvasHeightRatio) { canvasRescaleRatio = canvasWidthRatio; } const recalculatedInnerWidth = window.innerWidth / canvasRescaleRatio; const recalculatedInnerHeight = window.innerHeight / canvasRescaleRatio; if (!values.rectStartY) { values.rectStartY = objs.canvas.offsetTop + (objs.canvas.height - objs.canvas.height * canvasRescaleRatio) / 2; values.rect1X[2].start = (window.innerHeight / 2) / this.sceneInfo[this.currentScene].scrollHeight; values.rect1X[2].end = values.rectStartY / this.sceneInfo[this.currentScene].scrollHeight; values.rect2X[2].start = (window.innerHeight / 2) / this.sceneInfo[this.currentScene].scrollHeight; values.rect2X[2].end = values.rectStartY / this.sceneInfo[this.currentScene].scrollHeight; } objs.canvas.style.transform = `scale(${canvasRescaleRatio})`; objs.context.fillStyle = 'white'; objs.context.drawImage(objs.images[0], 0, 0); const whiteRectWidth = 0.15 * recalculatedInnerWidth; values.rect1X[0] = (objs.canvas.width - recalculatedInnerWidth) / 2; values.rect1X[1] = values.rect1X[0] - whiteRectWidth; values.rect2X[0] = values.rect1X[0] + recalculatedInnerWidth - whiteRectWidth; values.rect2X[1] = values.rect2X[0] + whiteRectWidth; objs.context.fillRect( parseInt(this.calcValues(values.rect1X, sceneScrollRatio)), 0, parseInt(whiteRectWidth), objs.canvas.height); objs.context.fillRect( parseInt(this.calcValues(values.rect2X, sceneScrollRatio)), 0, parseInt(whiteRectWidth), objs.canvas.height); if (sceneScrollRatio < values.rect1X[2].end) { objs.canvas.classList.remove('sticky'); } else { objs.canvas.classList.add('sticky'); objs.canvas.style.top = `-${(objs.canvas.height - objs.canvas.height * canvasRescaleRatio) / 2}px`; if (!values.blendHeight[1]) { values.blendHeight[0] = 0; values.blendHeight[1] = objs.canvas.height; values.blendHeight[2].start = values.rect1X[2].end; values.blendHeight[2].end = values.blendHeight[2].start + 0.2; } const blendImageheight = this.calcValues(values.blendHeight, sceneScrollRatio); objs.context.drawImage( objs.images[1], 0, objs.canvas.height - blendImageheight, objs.canvas.width, blendImageheight, 0, objs.canvas.height - blendImageheight, objs.canvas.width, blendImageheight); if (values.blendHeight[2].end < sceneScrollRatio) { if (!values.canvas_scale[0]) { values.canvas_scale[0] = canvasRescaleRatio; values.canvas_scale[1] = 0.5; values.canvas_scale[2].start = values.blendHeight[2].end values.canvas_scale[2].end = values.canvas_scale[2].start + 0.2; } objs.canvas.style.transform = `scale(${this.calcValues(values.canvas_scale, sceneScrollRatio)})`; if (values.canvas_scale[2].end < sceneScrollRatio) { objs.canvas.classList.remove('sticky'); objs.canvas.style.marginTop = `${0.4 * this.sceneInfo[this.currentScene].scrollHeight}px`; } else { objs.canvas.style.marginTop = 0; } } } break; } } calcValues(data, currPos) { let result; const [startVal, endVal, valRange] = data; const { start: startRange, end: endRange } = valRange; if (currPos <= startRange) { result = startVal; } else if (endRange <= currPos) { result = endVal; } else { result = startVal + (endVal - startVal) * (currPos - startRange) / (endRange - startRange) } return result; } prepareVideoImages() { let imgElem; for (let idx = 0; idx < 300; idx++) { imgElem = new Image(); imgElem.src = `./video/001/IMG_${6726 + idx}.JPG`; this.sceneInfo[0].objs.videoImages.push(imgElem); } let imgElem2; for (let idx = 0; idx < 960; idx++) { imgElem2 = new Image(); imgElem2.src = `./video/002/IMG_${7027 + idx}.JPG`; this.sceneInfo[2].objs.videoImages.push(imgElem2); } let imgElem3; const imgPaths = ['./images/blend-image-1.jpg', './images/blend-image-2.jpg'] for (const imgPath of imgPaths) { imgElem3 = new Image(); imgElem3.src = imgPath; this.sceneInfo[3].objs.images.push(imgElem3); } } loop() { this.delayedYOffset = this.delayedYOffset + (this.yOffset - this.delayedYOffset) * this.acc; if (!this.enterNewScene) { if (this.currentScene === 0 || this.currentScene === 2) { const sceneScrollHeight = this.delayedYOffset - this.prevScrollHeight; const sceneScrollRatio = sceneScrollHeight / this.sceneInfo[this.currentScene].scrollHeight; const values = this.sceneInfo[this.currentScene].values; const objs = this.sceneInfo[this.currentScene].objs; let imgIdx = parseInt(this.calcValues(values.canvas, sceneScrollRatio)); if (objs.videoImages[imgIdx]) { objs.context.drawImage(objs.videoImages[imgIdx], 0, 0); } } } this.rafId = requestAnimationFrame(this.loop.bind(this)); if (Math.abs(this.yOffset - this.delayedYOffset) < 1) { cancelAnimationFrame(this.rafId) this.rafState = false; } } checkMenu() { if (this.yOffset > 44) { this.sceneInfo[0].objs.localNav.classList.add('local-nav-sticky'); } else { this.sceneInfo[0].objs.localNav.classList.remove('local-nav-sticky'); } } addEventListener() { window.addEventListener('load', () => { document.body.classList.remove('before-load'); this.setLayout(); debugger; // this.sceneInfo[0].objs.context.drawImage(this.sceneInfo[0].objs.videoImages[0], 0, 0); this.yOffset = window.pageYOffset; console.log('yoffset', this.yOffset) let tempYOffSet = this.yOffset; let tempScrollCount = 0; if (tempYOffSet > 0) { let siId = setInterval(() => { window.scrollTo(0, tempYOffSet); tempYOffSet += 5; tempScrollCount++; if (tempScrollCount > 20) { clearInterval(siId); } }, 20) } window.addEventListener('scroll', () => { this.yOffset = window.pageYOffset; this.scrollLoop(); this.checkMenu(); if (!this.rafState) { this.rafId = requestAnimationFrame(this.loop.bind(this)); this.rafState = true; } }) document.querySelector('.loading').addEventListener('transitionend', (e) => { document.body.removeChild(e.currentTarget); }) }) } } const website = new Website(); # style.css html { font-size: 12px; font-family: 'Noto Sans KR', sans-serif; } body { overflow-x: hidden; position: relative; margin: 0; } div, span, article, section, header, footer, aside, p, ul, li, fieldset, legend, label, a, nav, form { box-sizing: border-box; } body * { overflow-x: hidden; } body.before-load { overflow: hidden; } .before-load .global-nav, .before-load .local-nav, .before-load .container, .before-load .footer { display: none; } .loading { position: fixed; display: flex; align-items: center; justify-content: center; top: 0; right: 0; bottom: 0; left: 0; z-index: 100; opacity: 0; background: white; transition: 2.0s; } .before-load .loading { opacity: 1; } @keyframes loading-circle-ani { 0% { stroke-dashoffset: 157; } 75% { stroke-dashoffset: -147; } 100% { stroke-dashoffset: -157; } } @keyframes loading-spin { 100% { transform: rotate(360deg); } } .loading-circle { width: 54px; height: 54px; animation: loading-spin 3s infinite; } .loading-circle circle { stroke: black; stroke-width: 4; stroke-dasharray: 157; stroke-dashoffset: 0; fill: transparent; animation: loading-circle-ani 3s infinite; } div { box-sizing: border-box; } a { text-decoration: none; color: black; } .global-nav { position: absolute; width: 100%; top: 0; left: 0; height: 44px; z-index: 5; } .global-nav-links, .local-nav-links { display: flex; align-items: center; justify-content: space-between; max-width: 1000px; margin: 0 auto; padding: 0 1rem; height: 100%; } .local-nav { position: absolute; width: 100%; left: 0; top: 50px; height: 55px; z-index: 6; border-bottom: 1px solid #888888; } .local-nav-sticky { position: fixed; top: 0; background: rgba(255, 255, 255, 0.1); backdrop-filter: saturate(180%) blur(15px); } .local-nav-right a { margin-left: 2em; } .product-name { font-size: 1.3rem; font-weight: 700; } .footer { position: relative; bottom: 0; height: 45px; width: 100%; background: orange; display: flex; align-items: center; justify-content: center; } .scroll-section { position: relative; padding-top: 50vh; width: 100%; margin: 0 auto; } .scroll-section h1 { font-size: 3rem; top: -10vh; text-align: center; position: relative; } .main-message { top: 45vh; font-size: 1.3rem; text-align: center; margin: 0 auto; opacity: 0; } .sticky-elem { position: fixed; left: 0; width: 100%; display: none; } .sticky-elem-canvas { top: 0; height: 100%; } .sticky-elem-canvas canvas { position: absolute; left: 50%; top: 50%; } .description { font-size: 1.3rem; max-width: 1000px; margin: 0 auto; padding: 0 1rem; } .description strong { font-size: 3em; float: left; margin-right: 0.5em; } .mid-message { font-size: 1.4rem; text-align: center; margin: 0 auto; padding-left: 1rem; padding-right: 1rem; } .mid-message .strong { font-size: 3em; font-weight: 600; } .image-blend-canvas.sticky{ position: fixed; top: 0; } #scroll-section-3 { display: flex; flex-direction: column; align-items: center; } .canvas-caption { font-size: 1.2rem; color: rgb(41, 37, 37); max-width: 1000px; margin: 0 auto; margin-top: -10vh; padding: 0 1rem; padding-bottom: 3rem; } .desc-message { font-size: 1rem; width: 20%; } #desc-message-a { left: 40%; top: 30%; } #desc-message-b { left: 40%; top: 25%; } #desc-message-c { left: 40%; top: 10%; } #show-scene-0 #scroll-section-0 .sticky-elem { display: block; } #show-scene-1 #scroll-section-1 .sticky-elem { display: block; } #show-scene-2 #scroll-section-2 .sticky-elem { display: block; } .pin { height: 10rem; width: 1px; background: black; } @media (min-width: 1024px) { html { font-size: 14px; } #scroll-section-0 h1 { font-size: 9vw; } .main-message { font-size: 4vw; } .description { font-size: 2rem; } .description strong { font-size: 6rem; } #scroll-section-2 .main-message { font-size: 6vw; } #scroll-section-2 .b { top: 20%; left: 53%; } #scroll-section-2 .c { left: 55%; } .main-message small { font-size: 1.5vw; } .desc-message { width: 20%; } .mid-message { font-size: 4vw; } .canvas-caption { margin-top: -8rem; padding: 0; font-size: 2rem; } }
- 해결됨애플 웹사이트 인터랙션 클론!
opacity:0, display:none의 차이가 무엇인가요?
둘 다 원하는 element를 보이지 않게 할 수 있는 걸로 알고 있습니다. 어떤 상황에 써야 하거나 연산량 차이가 있거나 한 부분이 있을까요?
- 미해결애플 웹사이트 인터랙션 클론!
애플 시퀀스 이미지 질문 드립니다
안녕하세요! 강의 너무 잘 보고 있습니다! 혹시 이번 아이패드 제품도 그렇고, 아이폰12 제품 페이지의 이미지 시퀀스가 일반적인 상태가 아니라 뭔가 암호화 된듯한? 형태인데 혹시 이유와 방법을 알 수 있을까요..?? 이런 질문을 어디에다 해야 하는지 고민하다가 질문 드립니다..!ㅠㅠ
- 미해결애플 웹사이트 인터랙션 클론!
익스플로러에서는 안돌아가나요?
익스플로러에서는 안돌아가나요? 만약안돌아간다면 어느부분에서 문제가 되는 것 일까요?ㅠㅠ
- 미해결애플 웹사이트 인터랙션 클론!
다했는데 업로드가문제예요ㅜㅜ
이게..사실 여기다 물어볼 일은 아니지만,, 도저히 못찾겠어서요ㅠㅠ 사진도 다 만들고 해서 페이지 완성을 했는데 깃허브에 용량이 크다고 올라가지 않아요ㅜ 혹시 방법이 있을까요? 어떻게 업로드하신건가요?
- 미해결애플 웹사이트 인터랙션 클론!
질문있습니다.
section을 3개로 하려고 했더니 오류가 나서 다시 4개로 하면 sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`; 이부분에서 Cannot read property 'style' of null 라는 오류가 나거나 아래와 같은 오류가 납니다. ㅇㅓ디가 잘못된걸까요..? ㅇ (() => { const sceneInfo = [ { //0 type: 'normal', heightNum: 5, //브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight : 0, objs:{ // html 객체 모음 container : document.querySelector('scroll-section-0') } }, { //1 type: 'sticky', heightNum: 5, //브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight : 0, objs:{ // html 객체 모음 container : document.querySelector('scroll-section-1') } }, { //2 type: 'sticky', heightNum: 5, //브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight : 0, objs:{ // html 객체 모음 container : document.querySelector('scroll-section-2') } }, { //3 type: 'sticky', heightNum: 5, //브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight : 0, objs:{ // html 객체 모음 container : document.querySelector('scroll-section-3') } } ]; function setLayout (){ //각 스크롤 섹션의 높이 세팅 for(let i = 0; i < sceneInfo.length; i++){ sceneInfo[i].scrollHeight = sceneInfo[i].heightNum*window.innerHeight; sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`; } } window.addEvnetListener('resize', setLayout); setLayout (); })();
- 미해결애플 웹사이트 인터랙션 클론!
canvas-caption의 margin에 관해서
안녕하세요. 우선 너무 좋은강의 잘 듣고있습니다. flex와 grid 수업을 듣고 수업이 너무 좋아서 애플클론코딩 강의까지 듣고있습니다. 다름이 아니라 제가 강의를 노트북 화면 반으로 나눠서 띄워놓고 듣는데 강의에서 처럼 canvas-caption의 margin을 margin: -8em, auto, 0; 으로 하니까 큰화면이 아닐시에 이렇게 차이가 크게 벌어져있습니다. 화면을 최대치로 키우면 간격이 확 줄어드는데 이렇게 화면을 축소하면 간격이 벌어집니다. 혹시나해서 완성본을 찾아보니 -8em이 아닌 -24rem으로 되어있어서 그렇게 해보니까 화면을 최대치로 키웠을때 글씨가 이미지와 겹치는 현상이 일어납니다. 강의에서 제가 놓친부분이 있는건지 어디서 잘못된건지 잘 모르겠습니다.. https://github.com/yoonsangkwak/Apple-clone
- 해결됨애플 웹사이트 인터랙션 클론!
offsetTop
scrollLoop 함수에서 prevScrollHeight 부분의 값을 offsetTop으로 지정해도 될 것 같아서 테스트 해보았는데요. 0번섹션에서 1번 섹션으로 넘어가는 순간 if문이 반복되면서 currentScene이 1이 아니라 2가 되는 것 같더라구요.. 왜 offsetTop으로 하면 안되는 걸까요? ㅜㅜ (() => { let yOffset = 0; // window.pageYOffset let prevScrollHeight = 0; // yOffset보다 이전에 위치한 섹션 높이 합 let currentScene = 0; // 현재 활성화된 scene const sceneInfo = [ { type: 'sticky', heightNum: 5, // 브라우저 높이의 5배로 scrollHiehgt setting scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-0'), }, }, { type: 'normal', heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-1'), }, }, { type: 'sticky', heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-2'), }, }, { type: 'sticky', heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-3'), }, } ]; function setLayout() { for (let i = 0; i < sceneInfo.length; i++) { sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`; } } function scrollLoop() { for (let i = 0; i < currentScene; i++) { prevScrollHeight = sceneInfo[i].objs.container.offsetTop; } if(yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) { currentScene++ } if(yOffset < prevScrollHeight) { currentScene-- } console.log(currentScene); } window.addEventListener('resize', setLayout); window.addEventListener('scroll', () => { yOffset = window.pageYOffset; scrollLoop(); }); setLayout(); })();
- 미해결애플 웹사이트 인터랙션 클론!
recalculatedInnerHeight 관련 질문
안녕하세요. `const recalculatedInnerHeight = document.body.offsetHeight / canvasScaleRatio;`로 설정되어 있습니다. canvas의 원래 크기가 변하지 않는 점을 이용해서 rect의 높이를 `objs.canvas.height`로 사용하면 안 될까요? 왜 body의 offsetHeight로 설정하셨는지가 궁금합니다.
- 미해결애플 웹사이트 인터랙션 클론!
안녕하세요!!!!
안녕하세요 인터랙션 공부할려고 수강한 사람입니다!! 선생님 덕분에 재밌게 입문했습니다!! 다름이 아니라 제가 연습을 하고 있는데 오류가 나서 무슨 오류인지도 잘 모르겠어서 이렇게 질문드립니다ㅠㅠ https://github.com/cks612/web_practice 혹시 코드 한 번만 봐주실 수 있을까요..?ㅠㅠ sceneInfo 2에서 문제가 발생하는 거 같은데 해결을 못 하고 있습니다...
- 해결됨애플 웹사이트 인터랙션 클론!
padding-top <-> height 질문입니다.
1번째 container에 height가 `heightNum`값에 따라서 5815px로 설정되었습니다. 근데 이 때, padding-top이 50vh로 설정되었는데 5815px 안에 포함되더라구요. 다른 페이지를 열어서 padding-top을 따로 줬을 때는 height + padding-top이 되어서 어떤 원리로 height 내에 padding이 포함된 것인지 궁금합니다.
- 해결됨애플 웹사이트 인터랙션 클론!
values, objs 관리 디자인 패턴 질문
animation이 많은 경우, values, objs를 hard-coding 해서 사용하면 관리 비용이 많이 들 것 같습니다. 혹시 좀 더 큰 프로젝트 하실 때 사용하시는 패턴이 있으실까요?
- 미해결애플 웹사이트 인터랙션 클론!
main-message, sticky-elem CSS 상 위치에 따른 display 변경
sticky-elem이 main-message보다 style.css 상에 위에 존재하게 되면 display: none이 적용되지 않는 것을 확인했습니다. CSS 페이지 내에서 class 순서가 눈에 잘 들어오지 않는데(자주 실수할 것 같은 부분입니다.) 이 부분을 좀 더 효과적으로 관리할 수 있는 방법이 있을까요?
- 해결됨애플 웹사이트 인터랙션 클론!
container 위치 질문드립니다.
안녕하세요. 강의 잘 듣고 있습니다. 강의를 듣고 CSS부터 만들어보고 있는데 궁금한 점이 있어서 질문드립니다. global-nav, local-nav의 position이 absolute인데 왜 container는시작을 중간부터 시작하나요? body의 가장 위부터 시작할 것 같은데 왜 아닌지 도저히 모르겠어서 질문드립니다.