묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결애플 웹사이트 인터랙션 클론!
[스크롤 높이 세팅] scrollHeight값이 3990아닌 4645로만 나오는데 뭐가 문제일까요? ㅠㅠ
(() => { const sceneInfo = [ { // 0 type: 'sticky', heightNum: 5, // 브라우저 높이의 5배로 scrollHeight 세팅(디바이스 마다 높이가 다르기에 각 기계가 가진 높이를 불러오고 곱해준다.) scrollHeight: 0, }, { // 1 type: 'normal', heightNum: 5, scrollHeight: 0, }, { // 2 type: 'sticky', heightNum: 5, scrollHeight: 0, }, { // 3 type: 'sticky', heightNum: 5, scrollHeight: 0, } ]; function setLayout() { //각 스크롤 섹션의 높이 세팅 for (let i = 0; i < sceneInfo.length; i++) { sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; } console.log(sceneInfo); } setLayout(); })(); 4645값으로 그대로 진행해도 될까요?
-
미해결애플 웹사이트 인터랙션 클론!
scrollLoop 함수 질문
const scrollLoop = () => { prevScroll = 0; // 스크롤값이 다시 0으로 시작하게 for (let i = 0; i < current; i++) { prevScroll += sceneInfo[i].scrollHeight; } console.log('prev', prevScroll) if (currentScroll > prevScroll + sceneInfo[current].scrollHeight) { current++; alert() } console.log('next', prevScroll) if (currentScroll < prevScroll) { current--; } };선생님 여기서 prevScroll에 for문으로 돌때 sceneInfo[i].scrollHeight; 여기서 이미 값을 더해줄텐데 왜 if (currentScroll > prevScroll) { current++; alert() }조건문에 저렇게 하면 작동이 안돼나요 제가 이해를 잘못하고 있는지 ...
-
미해결지금 바로 D3.js 시작하기 : 웹 데이터 시각화
foreach 문 자동완성하는 익스텐션 알려주세요!
foreach 문 자동완성하는 익스텐션 알려주세요!
-
미해결애플 웹사이트 인터랙션 클론!
load 이벤트시 첫 비디오 이미지가 뜨네요.
window.addEventListener('load' ... 중략sceneInfo[0].objs.context.drawImage(sceneInfo[0].objs.videoImages[0], 0, 0);안녕하세요.작업하다 이상해서, 강의자료도 같이 확인해보니 중간에 새로고침시 videoImages의 첫 이미지가 잠시 뜨는데, 아마 위의 코드 때문이 아닌가 합니다. 저 부분은 첫 scene의 장면임에도 불구하고, 뜬다는 것은 찰나이긴 하지만, 첫영역부터 순간적으로 중간으로 이동하면서 나타나는 현상같기도 한데, 이게 컴퓨터의 성능차이일수도 있다는 생각이 들기도 합니다. 저만 이런건지 모르겠습니다만..저 코드를 제거하고 중간에서 새로고침해보니 안보여서 문제가 없지만, 최상단 첫화면에서 새로고침하면 이미지가 안보이니 또 문제더군요.그래서 위 코드를 제거후, 새로고침시 조금 스크롤되는 코드에서 추가 조건문(else)를 붙여서 아래와 같이 수정해보았습니다. if (tempYOffset > 0) { let siId = setInterval(() => { scrollTo(0, tempYOffset); tempYOffset += 5; if (tempScrollCount > 20) { clearInterval(siId); } tempScrollCount++; }, 20); } else { sceneInfo[0].objs.context.drawImage(sceneInfo[0].objs.videoImages[0], 0, 0); } 이렇게 처리하니 문제가 없는 듯 한데, 이게 저한테만 생기는 문제인지, 아니라면 제가 위와 같이 처리한 방법이 맞는것인지 궁금합니다.
-
미해결애플 웹사이트 인터랙션 클론!
마지막 강의 js파일로 바꿀 시 스크롤 프레임 렉? 버벅임?
마지막 강의에서 올리신 js파일로 바꾸니 애니메이션이 들어가있는 section 0, 2에서 터치패드로 스크롤 시 멈추는 부분에서 몇프레임 앞뒤 왔다갔다 하는 현상이 좀 심하게 발생합니다. 이전만큼 부드럽게 진행이 되지 않고 좀 간격이 넓게 스크롤 되는 느낌입니다. 이미지 로딩 후 정렬 때문에 이렇게 되는건가요? 아니라면 혹시 팁을 좀 주실 수 있나요?다시 확인해본 바 새로고침을 하면 이러한 문제가 사라지긴 합니다만 원인을 잘 모르겠네요. 좀 더 알아보겠습니다. 제가 진행한 파일입니다!https://leealvinlee.github.io/Leeum_Maurizio_Catellan/
-
미해결애플 웹사이트 인터랙션 클론!
선생님 이해가 잘 안가는 문구가 있어서 질문 드립니다.
setLayout 함수 끝에 들어가는${sceneInfo[i].scrollHeight}px;부분에서 따옴표로 잘못 작성해서 좀 해맸었습니다 ㅎㅎ;${}부분을 풀어 쓰면 정확하게 어떤식으로 표기가 되는건지.. 궁금합니다.제 예상으로는 for문 안에 따로 변수를 선언하지 않고, 계산된 값을 바로 가져오기 위해 사용한 것 같은데 맞을까요..??
-
미해결애플 웹사이트 인터랙션 클론!
영상 속 코드와 배포하신 소스 코드가 다르네요.
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { margin: 0; padding: 0; } body { height: 500vh; } .sample-video { position: fixed; top: 0; left: 0; width: 100%; } </style> </head> <body> <div class="container"> <!-- <video class="sample-video" src="../video/sample-low.mp4" muted></video> --> <video class="sample-video" src="../video/sample-high.mp4" muted></video> </div> <script> const videoElem = document.querySelector('.sample-video'); let videoDuration; videoElem.addEventListener('loadeddata', function() { console.log('비디오 로드 완료'); videoDuration = videoElem.duration; init(); }) let progress; let currentFrame; function init() { window.addEventListener('scroll', function () { progress = pageYOffset / (document.body.offsetHeight - window.innerHeight); console.log(progress); if (progress < 0) progress = 0; if (progress > 1) progress = 1; requestAnimationFrame(function() { videoElem.currentTime = videoElem.duration * progress; }) }); } </script> </body> </html>복붙해서 사용하세요.
-
미해결애플 웹사이트 인터랙션 클론!
pageYOffset || scrollY
안녕하세요-!선생님께서 pageYOffset을 사용하셨는데, 그 이유가 IE에서도 호환되기하기 위함이 맞을까요?IE를 고려하지 않는다면 scrollY를 사용하는게 맞는지 궁금합니다.
-
해결됨애플 웹사이트 인터랙션 클론!
messageA_opacity_out 글자가 사라지지 않는 문제
강사님 안녕하세요. 특정 타이밍 스크롤 애니메이션 적용하기 2번째 시간 transform 적용하기 전까지 들었습니다. (13분쯤 ) opacity: 1;로 글자가 점점 나타나는 부분까지는 했는데 글자가 사라지지 않아 console 로 messageA_opacity_out을 찍어봤습니다. console 창에서는 messageA_opacity_out 숫자가 제대로 줄어드는데 글자는 사라지지가 않습니다. 앞뒤로 강의를 돌려서 반복해서 다시 들었으나 문제점을 찾지 못하여 질문을 드립니다. 답변해주시면 감사합니다! https://florentine-trombone-82f.notion.site/0398c6579ce64b948fae207605e623ad 따로 파일을 올릴 수 없어 페이지에 올려둡니다
-
미해결애플 웹사이트 인터랙션 클론!
overflow-x:hidden을 사용했는데, 모바일 화면에서 우측이 잘리고 가로 스크롤이
d위 이미지처럼 가로 스크롤이 생기는 데 overflow-x: hidden 기능이 적용되지 않아서 생기는 문제일까요?
-
미해결애플 웹사이트 인터랙션 클론!
스크롤이 적용이 안되는것같아요!ㅜㅜ
스크롤도 늘어나질 않고 스티키도 안보이고 씬도 변하질 않고 계속 0에 있어요ㅜㅜ 첨부하겠습니당 const main = () => { let y0ffset = 0; const sceneInfo = [ { //0 type: 'sticky', heightNum: 5, //브라우저 높이의 5배로 scrollHeight 세팅 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 = `${sceneInfo[i].scrollHeight}px`; } console.log(sceneInfo); } function scrollLoop() { } window.addEventListener('resize',setLayout); window.addEventListener('scroll',() => { y0ffset = window.pageXOffset; scrollLoop(); }); setLayout(); }; main();
-
해결됨애플 웹사이트 인터랙션 클론!
if (scrollRatio <= 0.22) 로 이벤트 발생 시점을 컨트롤 하는 부분과 values 에 있는 start 와 end 값
if (scrollRatio <= 0.22) 로 이벤트 발생 시점을 컨트롤 하는 부분과 values 에 있는 start 와 end 로 처리하는 부분이 동일하지 않나요?단순히 calcValues 도 하지 않기 위해서 if (scrollRatio <= 0.22) 와 같은 조건문이 있는걸까요?
-
미해결애플 웹사이트 인터랙션 클론!
이미지가 안보입니다...ㅠ(도움이 필요합니다!! 꼭 봐주세요!)
안녕하세요 선생님 강의 잘 듣고 있습니다! 다름이 아니라 밑에 수강생분 처럼 저도 이미지가 안보이고 Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLCanvasElement or HTMLImageElement or HTMLVideoElement or ImageBitmap or OffscreenCanvas or SVGImageElement or VideoFrame)'. 이런 에러가 계속 뜨는데.. 어떻게 해야할지 모르겠습니다..ㅠㅠㅠ 분명 계속 이미지 경로도 확인하고 문제가 없는거같은데...대체 뭐가 문제인지 도움이 필요합니다..!! 너무 해결이 안되니까 꼭...해결하고싶습니다.. 잘되던 텍스트 애니메이션도 작동이 안됩니다. 보니까 playanimaion함수에서 sequence도 console.log에 찍으면 undefined로 찍히는데 도저히 뭐가 잘못 됐는지 모르겠습니다. 선생님이 주신 완성 코드를 대입해도 안뜨네요ㅠㅠㅠ (() => { let yOffset = 0; // window.pageYOffset대신 쓸 변수 let prevScrollHeight = 0; //현재 스크롤 위치(yOffset)보다 이전에 위치한 스크롤 섹션들의 높이값의 합 let currentScene = 0; //현재 활성화된(눈 앞에 보고있는) 씬(scroll-section) // sceneInfo는 애니메이션을 처리하는 정보를 담아놓는 변수 let enterNewScene = false;//새로운 scene이 시작되는 순간 true로 바뀜 const sceneInfo = [ { //0 type: 'sticky', heightNum:5,//브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight: 0, // 애니메이션을 조작할 오브젝트를 objs에 담음 objs:{ container: document.querySelector('#scroll-section-0'), messageA: document.querySelector('#scroll-section-0 .main-message.a'), messageB: document.querySelector('#scroll-section-0 .main-message.b'), messageC: document.querySelector('#scroll-section-0 .main-message.c'), messageD: document.querySelector('#scroll-section-0 .main-message.d'), canvas: document.querySelector('#video-canvas-0'), //context객체의 메서드인 getContext를 인자 2d를 넣어 호출해 그림을 그려주는 기초를 연다. context: document.querySelector('#video-canvas-0').getContext('2d'), //비디오 이미지를 배열에 넣을 예정이다. videoImages: [] }, //오브젝트를 어느시점에 보이고 안보이게 할지를 담을 곳 css제어 //시작값 :0 끝값 :1 values: { videoImageCount: 300, //스크롤에 따른 이미지 순서의 [초기값,최종값] messageA와 다르게 섹션의 부분에서 실행될 이미지가 아닌 한스크롤에서 쭉 이어지므로 start,end구간이 필요없다 imageSequence:[0 , 299], messageA_opacity_in: [0, 1, { start: 0.1, end: 0.2 }], messageB_opacity_in: [0, 1, { start: 0.3, end: 0.4 }], //30%~ 40% messageC_opacity_in: [0, 1, { start: 0.5, end: 0.6 }], messageD_opacity_in: [0, 1, { start: 0.7, end: 0.8 }], // 글자를 20%에서 0으로 가는건데 %는 css에 나중에 작업할 예정 세번째 인수에는 타이밍이 들어간다(=어느 타이밍에 효과를 줄것인가) messageA_translateY_in: [20, 0, { start: 0.1, end: 0.2 }], messageB_translateY_in: [20, 0, { start: 0.3, end: 0.4 }], messageC_translateY_in: [20, 0, { start: 0.5, end: 0.6 }], messageD_translateY_in: [20, 0, { start: 0.7, end: 0.8 }], messageA_opacity_out: [1, 0, { start: 0.25, end: 0.3 }], messageB_opacity_out: [1, 0, { start: 0.45, end: 0.5 }], messageC_opacity_out: [1, 0, { start: 0.65, end: 0.7 }], messageD_opacity_out: [1, 0, { start: 0.85, end: 0.9 }], messageA_translateY_out: [0, -20, { start: 0.25, end: 0.3 }], messageB_translateY_out: [0, -20, { start: 0.45, end: 0.5 }], messageC_translateY_out: [0, -20, { start: 0.65, end: 0.7 }], messageD_translateY_out: [0, -20, { start: 0.85, end: 0.9 }] // messageA_opacity:[200,900] } }, { //1 type: 'normal', //heightNum은 scrollHeight를 결정할 때 innerheight의 몇배로 할지 정해주는 수 였는데 normal은 원래 본인 default높이로 설정하므로 필요가 없다 // heightNum:5, scrollHeight: 0, objs:{ container: document.querySelector('#scroll-section-1') } }, { //2 type: 'sticky', heightNum:5, scrollHeight: 0, objs:{ container: document.querySelector('#scroll-section-2'), messageA: document.querySelector('#scroll-section-2 .a'), messageB: document.querySelector('#scroll-section-2 .b'), messageC: document.querySelector('#scroll-section-2 .c'), pinB: document.querySelector('#scroll-section-2 .b .pin'), pinC: document.querySelector('#scroll-section-2 .c .pin') }, values: { messageA_translateY_in: [20, 0, { start: 0.15, end: 0.2 }], messageB_translateY_in: [30, 0, { start: 0.5, end: 0.55 }], messageC_translateY_in: [30, 0, { start: 0.72, end: 0.77 }], messageA_opacity_in: [0, 1, { start: 0.15, end: 0.2 }], messageB_opacity_in: [0, 1, { start: 0.5, end: 0.55 }], messageC_opacity_in: [0, 1, { start: 0.72, end: 0.77 }], messageA_translateY_out: [0, -20, { start: 0.3, end: 0.35 }], messageB_translateY_out: [0, -20, { start: 0.58, end: 0.63 }], messageC_translateY_out: [0, -20, { start: 0.85, end: 0.9 }], messageA_opacity_out: [1, 0, { start: 0.3, end: 0.35 }], messageB_opacity_out: [1, 0, { start: 0.58, end: 0.63 }], messageC_opacity_out: [1, 0, { start: 0.85, end: 0.9 }], pinB_scaleY: [0.5, 1, { start: 0.5, end: 0.55 }], pinC_scaleY: [0.5, 1, { start: 0.72, end: 0.77 }], pinB_opacity_in: [0, 1, { start: 0.5, end: 0.55 }], pinC_opacity_in: [0, 1, { start: 0.72, end: 0.77 }], pinB_opacity_out: [1, 0, { start: 0.58, end: 0.63 }], pinC_opacity_out: [1, 0, { start: 0.85, end: 0.9 }] } }, { //3 type: 'sticky', heightNum:5, scrollHeight: 0, objs:{ container: document.querySelector('#scroll-section-3') } } ]; function setCanvasImages() { let imgElem; for (let i = 0; i < sceneInfo[0].values.videoImageCount; i++){ //이미지 객체가 만들어짐 new Image(); 대신에 document.createElement('img')랑 같은말이다. imgElem = new Image(); imgElem.src = `./video/001/IMG_${6726 + i}.JPG`; sceneInfo[0].objs.videoImages.push(imgElem); } } setCanvasImages(); function setLayout() { //각 스크롤 섹션의 높이 세팅 for (let i = 0; i < sceneInfo.length; i++){ if (sceneInfo[i].type === 'sticky') { sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; } else if (sceneInfo[i].type === 'normal') { //container본연의 높이로 가져와서 그 크기만큼으로만 normal부분을 설정한다. sceneInfo[i].scrollHeight = sceneInfo[i].objs.container.offsetHeight; } sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`; } yOffset = window.pageYOffset; let totalScrollHeight = 0; for (let i = 0; i < sceneInfo.length; i++){ totalScrollHeight += sceneInfo[i].scrollHeight; if (totalScrollHeight >= yOffset) { currentScene = i; break; } } // 변수랑 문자열이 섞여있으면 ``을 써야한다. document.body.setAttribute('id', `show-scene-${currentScene}`); // console.log(sceneInfo); //초기화 할 때 실행되는 setLayout은 currentScene이 정해지지 않은 상태에서 currentScene을 구하는 것이고, // 스크롤 할 때마다 실행되는 scrollLoop는 현재 활성화된 currentScene 까지의 스크롤양(prevScrollHeight)을 기준으로 일정량의 스크롤이 지나갔을 때 currentScene을 +1 또는 -1 하는 것이랍니다. } function calcValues(values, currentYOffset) { //여기서 인자 currentYOffset은 현재 씬에서 얼마나 스크롤 됐는지 //현재 섹션에서 얼마나 스크롤 되었는지를 비율로 구한다 스클로의 비율을 0~1사이로 정한다. 비율을 구해서 그 값을 css값에 대입해준다. //yoffset변수는 전체 레이아웃에서 스크롤이 어디 위치해있는지 알려주는 변수이다. 현재 씬에서 스크롤이 얼마나됐는지는 알 수 없다. let rv; const scrollHeight = sceneInfo[currentScene].scrollHeight; //현재 씬(스크롤섹션)에서 스크롤된 범위를 비율로 구하기 ==>let scrollRation = 현재 씬에서 스크롤된 값 / 현재 씬 전체 const scrollRatio = currentYOffset / sceneInfo[currentScene].scrollHeight; if (values.length === 3) { // start~end사이에 애니메이션 실행 const partScrollStart = values[2].start * scrollHeight; const partScrollEnd = values[2].end * scrollHeight; const partScrollHeight = partScrollEnd - partScrollStart; if (currentYOffset >= partScrollStart && currentYOffset <= partScrollEnd) { rv = (currentYOffset - partScrollStart) / partScrollHeight * (values[1] - values[0]) + values[0]; } else if (currentYOffset < partScrollStart) { //스크롤섹션에서 스크롤이 start에 도달하지 못했을 때 rv = values[0]; //opacity가 0이라는 뜻 } else if (currentYOffset > partScrollEnd) { //스크롤섹션에서 스크롤이 end를 완전히 벗어났을 때 rv = values[1]; //opacity가 1이라는 뜻 } else { rv = scrollRatio * (values[1] - values[0]) + values[0]; } // rv = parseInt(scrollRatio * (values[1] - values[0]) + values[0]); return rv; } } //애니메이션 함수 function playAnimation() { // 모든 섹션에 애니메이션을 주는 것은 비효율적이므로 switch문을 이용해서 현재 보고있는 섹션의 요소에만 애니메이션을 준다 const objs = sceneInfo[currentScene].objs; const values = sceneInfo[currentScene].values; const currentYOffset = yOffset - prevScrollHeight; const scrollHeight = sceneInfo[currentScene].scrollHeight; const scrollRatio = currentYOffset / sceneInfo[currentScene].scrollHeight; switch (currentScene) { case 0: let sequence = calcValues(values.imageSequence, currentYOffset); objs.context.drawImage(objs.videoImages[sequence], 0, 0); console.log(sequence); if (scrollRatio <= 0.22) { // in objs.messageA.style.opacity = calcValues(values.messageA_opacity_in, currentYOffset); objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageA.style.opacity = calcValues(values.messageA_opacity_out, currentYOffset); objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.42) { // in objs.messageB.style.opacity = calcValues(values.messageB_opacity_in, currentYOffset); objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageB.style.opacity = calcValues(values.messageB_opacity_out, currentYOffset); objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.62) { // in objs.messageC.style.opacity = calcValues(values.messageC_opacity_in, currentYOffset); objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageC.style.opacity = calcValues(values.messageC_opacity_out, currentYOffset); objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.82) { // in objs.messageD.style.opacity = calcValues(values.messageD_opacity_in, currentYOffset); objs.messageD.style.transform = `translate3d(0, ${calcValues(values.messageD_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageD.style.opacity = calcValues(values.messageD_opacity_out, currentYOffset); objs.messageD.style.transform = `translate3d(0, ${calcValues(values.messageD_translateY_out, currentYOffset)}%, 0)`; } break; case 2: // console.log('2 play'); if (scrollRatio <= 0.25) { // in objs.messageA.style.opacity = calcValues(values.messageA_opacity_in, currentYOffset); objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageA.style.opacity = calcValues(values.messageA_opacity_out, currentYOffset); objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.57) { // in objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_in, currentYOffset)}%, 0)`; objs.messageB.style.opacity = calcValues(values.messageB_opacity_in, currentYOffset); objs.pinB.style.transform = `scaleY(${calcValues(values.pinB_scaleY, currentYOffset)})`; } else { // out objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_out, currentYOffset)}%, 0)`; objs.messageB.style.opacity = calcValues(values.messageB_opacity_out, currentYOffset); objs.pinB.style.transform = `scaleY(${calcValues(values.pinB_scaleY, currentYOffset)})`; } if (scrollRatio <= 0.83) { // in objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_in, currentYOffset)}%, 0)`; objs.messageC.style.opacity = calcValues(values.messageC_opacity_in, currentYOffset); objs.pinC.style.transform = `scaleY(${calcValues(values.pinC_scaleY, currentYOffset)})`; } else { // out objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_out, currentYOffset)}%, 0)`; objs.messageC.style.opacity = calcValues(values.messageC_opacity_out, currentYOffset); objs.pinC.style.transform = `scaleY(${calcValues(values.pinC_scaleY, currentYOffset)})`; } break; case 3: // console.log('3 play'); break; } } // keyframe이란: 애니메이션이 진행중에 변화가 있는 지점을 keyframe이라고 한다. function scrollLoop() { enterNewScene = false; prevScrollHeight = 0;//누적을 막기 위해서 // 현재 보고잇는 section이 몇번째 section인지 판별하려고함 for (let i = 0; i < currentScene; i++){ prevScrollHeight = prevScrollHeight + sceneInfo[i].scrollHeight; } // console.log(prevScrollHeight); if (yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) { enterNewScene = true; currentScene++; document.body.setAttribute('id', `show-scene-${currentScene}`); // setLayout함수에서 이미 id값이 정해져있지만 스크롤에 변함에 따라 체크하는 방식으로 쓴다 } if (yOffset < prevScrollHeight) { if (currentScene === 0) return;//브라우저 바운스 효과로 인해 마이너스가 되는 것을 방지(모바일) enterNewScene = true; currentScene--; document.body.setAttribute('id', `show-scene-${currentScene}`); } // console.log(currentScene); // enterNewScene이 참이라는것은 씬이 바뀌는순간인 것이고 참일때 return, 즉 playAnimation함수를 한타임 실행하지 않는다는것으로 음수값이 무시되고 다시 playAnimation함수가 실행된다. if (enterNewScene) return; playAnimation(); } window.addEventListener('scroll', () => { yOffset = window.pageYOffset; scrollLoop(); }); window.addEventListener('load', setLayout); // window의 모든 문서(html, 이미지, 동영상..)가 완료되고 나서 // 레이아웃 잡는 setLayout함수를 실행한다 // window.addEventListener('DOMContentLoaded', setLayout); //반대로 window의 html구조들만 완료되면 반대로 이미지는 로드되지않아도 setlayout함수를 실행하는것 //인데 load보다는 빨리 setlayout을 실행한다. window.addEventListener('resize', setLayout); // 윈도우가 리사이즈 되었을 때 setLayout을 호출한다 })(); <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>AirMug Pro</title> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;900&display=swap" rel="stylesheet"> <link rel="stylesheet" href="css/default.css"> <link rel="stylesheet" href="css/main.css"> <script defer src="js/main.js"></script> </head> <body> <div class="container"> <nav class="global-nav"> <div class="global-nav-links"> <a href="#" class="global-nav-item">Rooms</a> <a href="#" class="global-nav-item">Ideas</a> <a href="#" class="global-nav-item">Stores</a> <a href="#" class="global-nav-item">Contact</a> </div> </nav> <nav class="local-nav"> <div class="local-nav-links"> <a href="#" class="product-name">AirMug Pro</a> <a href="#">개요</a> <a href="#">제품사양</a> <a href="#">구입하기</a> </div> </nav> <section class="scroll-section" id="scroll-section-0"> <h1>AirMug Pro</h1> <!-- p태그의 값들을 sticky, 즉 스티커처럼 고정해놓고 스트롤의 값에 따라 보여지도록 p태그를 감싸고 있는 div에 class를 준다. --> <div class="sticky-elem sticky-elem-canvas"> <canvas id ="video-canvas-0" width="1920px" height="1080px"></canvas> </div> <div class="sticky-elem main-message a"> <p>온전히 빠져들게 하는<br>최고급 세라믹</p></div> <div class="sticky-elem main-message b"> <p>주변 맛을 느끼게 해주는<br>주변 맛 허용 모드</p> </div> <div class="sticky-elem main-message c"> <p>온종일 편안한<br>맞춤형 손잡이</p> </div> <div class="sticky-elem main-message d"> <p>새롭게 입가를<br>찾아온 매혹</p> </div> </section> <section class="scroll-section" id="scroll-section-1"> <p class="description"> <strong>보통 스크롤 영역</strong> Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas minus praesentium incidunt tempora repellendus? Laudantium sed excepturi quo nemo quisquam totam, cumque nam sit incidunt, corrupti libero temporibus obcaecati enim facilis dolore itaque minima dolorem sequi explicabo. Modi alias in ullam labore, minima voluptates ipsa ex laborum deleniti tempora incidunt architecto iure aut, molestias ab! Fugiat aliquam veritatis ad voluptatibus, quaerat officia doloribus aliquid nulla repellat quidem rem magni perferendis labore similique reprehenderit aut. Cum quis omnis ex iusto natus iste nisi magnam, nesciunt fugit aspernatur ullam? Temporibus, dolores ipsa. Odit odio aliquam sunt error corporis ab facere, illum sequi! Explicabo nulla, ipsum necessitatibus laboriosam eligendi aspernatur quam nostrum. Debitis est ad, vel in corrupti voluptas laboriosam quo, reiciendis quos quas culpa, atque voluptate tempora tempore quam accusamus ipsa asperiores voluptatibus adipisci non vero aspernatur eum nesciunt quis. A cum, molestiae eaque repellat similique esse eligendi reprehenderit amet omnis minus quae voluptatum delectus dolores repudiandae? Distinctio aut aspernatur, iure nemo, eum soluta error necessitatibus minus voluptatum laborum rem dignissimos quae. Error ut recusandae ullam magnam quas molestiae repellat culpa. Ab nihil sint voluptatum in? Enim suscipit debitis earum culpa sint maxime hic, repudiandae nulla ad error voluptatem fugiat blanditiis odit! </p> </section> <section class="scroll-section" id="scroll-section-2"> <div class="sticky-elem main-message a"> <p> <small>편안한 촉감</small> 입과 하나 되다 </p> </div> <div class="sticky-elem desc-message b"> <p> 편안한 목넘김을 완성하는 디테일한 여러 구성 요소들, 우리는 이를 하나하나 새롭게 살피고 재구성하는 과정을 거쳐 새로운 수준의 머그, AirMug Pro를 만들었습니다. 입에 뭔가 댔다는 감각은 어느새 사라지고 오롯이 당신과 음료만 남게 되죠. </p> <div class="pin"></div> </div> <div class="sticky-elem desc-message c"> <p> 디자인 앤 퀄리티 오브 스웨덴,<br>메이드 인 차이나 </p> <div class="pin"></div> </div> </section> <section class="scroll-section" id="scroll-section-3"> <p class="mid-message"> <strong>Retina 머그</strong><br> 아이디어를 광활하게 펼칠<br> 아름답고 부드러운 음료 공간. </p> <p class="canvas-caption"> Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet at fuga quae perspiciatis veniam impedit et, ratione est optio porro. Incidunt aperiam nemo voluptas odit quisquam harum in mollitia. Incidunt minima iusto in corporis, dolores velit. Autem, sit dolorum inventore a rerum distinctio vero illo magni possimus temporibus dolores neque adipisci, repudiandae repellat. Ducimus accusamus similique quas earum laborum. Autem tempora repellendus asperiores illum ex! Velit ea corporis odit? Ea, incidunt delectus. Sapiente rerum neque error deleniti quis, et, quibusdam, est autem voluptate rem voluptas. Ratione soluta similique harum nihil vel. Quas inventore perferendis iusto explicabo animi eos ratione obcaecati. </p> </section> <footer class="footer"> 2020, 1분코딩 </footer> </div> </body> </html>
-
미해결애플 웹사이트 인터랙션 클론!
공간을 넘어가요!
공간을 넘어가서 lorem 값이 나오는데 왜 이러는지 알 수 있을까요...?! padding 값이 문제일까요 ??? 문제는 없는 것 같은데 원래 넘어갈 수 밖에 없는 건지 궁금합니다.
-
미해결
웹크롤링 질문입니다.
일하다가 반복 단순 작업이 있어 웹크롤링을 만들려고 하는데 정보가 없어 질문을 드립니다. 각 태그가 svg안에 rect안에 text안에 tspan태그로 html이 만들어져있는데 이것을 리스트로 뽑아서 각 문자별로 해보려고 하다가 어려워서 질문을 올립니다 tspan 안의 문자로된 글씨를 크롤링하려면 어떻게 해야하는지 설명이 가능할까요?
-
미해결Axure RP 9,10 - 서비스 기획자를 위한 최적의 프로토타이핑 툴
Figma에서 만든 SVG 파일을 Axure로 불러오는 건에 대하여
안녕하세요. 선생님. 잘 지내고 계신가요. 작업을 진행하다가 고민이 생겨 질문 드립니다. 최근 Figma 활용법을 익히며 해당 툴로 필요한 UI를 그리기 시작했는데요. 아무래도 인터렉션이나 다이나믹 구현은 Axure가 훨씬 좋아서 Figma로 그린 UI를 Plugin방식으로 Axure로 가져와 활용하고 있습니다. 그런데 좀 복잡한 UI들은 Axure로 Plugin하는 과정에서 원본과 좀 다른 모양으로 가져와지더군요. 두 개 툴 사이에 기능적 호환성이 그다지 높은 것 같지는 않은데, 일단 이 경우는 Figma에서 그린 UI를 SVG파일로 Export하여 Axure로 불러오고 있습니다. 그런데 Axure 캔버스로 가져온 해당 SVG 파일의 이미지를 확대해보니 마치 PNG파일 마냥 이미지의 아웃라인이 비트맵(Bitmap) 방식으로 쪼개지더군요. Axure Preview나 Cloud에서는 또 안 그런 것 같아서 '그냥 그런가보다' 하고 작업을 진행하고 있는데... 이게 영 찜찜하네요. 이게 Axure 캔버스 상에서 보여지는 단순한 오류인 건지, 아니면 SVG 파일을 Axure로 불러오는 제 방식에 문제가 있는 것인지 잘 모르겠습니다. 답변 주시면 감사하겠습니다. 오늘도 좋은 하루 되세요!
-
미해결SVG 마스터
글씨 깨짐
맥북을 사용하고 있는데 제 컴퓨터 에서는 텍스트 애니메이션이 잘 작동하지만 윈도우로 봤을때에는 폰트적용이 안되고 깨진듯이 나옵니다. 폰트 적용법 알수있을까요!
-
미해결애플 웹사이트 인터랙션 클론!
main-add.js 코드 오류 있습니다. 이거 보고 수정하세요
main-add.js 내용 적용했는데 section-2 부분의 messageC부분이 section3까지 넘어가서 뭔가 이상하다고 생각했습니다. 실제로, 강의 영상에 나온 것이랑 값이 차이나서 수정한 부분 올립니다. sceneInfo의 section2부분의 value만 변경해주시면 됩니다. values: { messageA_translateY_in: [20, 0, { start: 0.15, end: 0.2 }], messageB_translateY_in: [30, 0, { start: 0.5, end: 0.55 }], messageC_translateY_in: [30, 0, { start: 0.72, end: 0.77 }], messageA_opacity_in: [0, 1, { start: 0.15, end: 0.2 }], messageB_opacity_in: [0, 1, { start: 0.5, end: 0.55 }], messageC_opacity_in: [0, 1, { start: 0.72, end: 0.77 }], messageA_translateY_out: [0, -20, { start: 0.3, end: 0.35 }], messageB_translateY_out: [0, -20, { start: 0.58, end: 0.63 }], messageC_translateY_out: [0, -20, { start: 0.85, end: 0.9 }], messageA_opacity_out: [1, 0, { start: 0.3, end: 0.35 }], messageB_opacity_out: [1, 0, { start: 0.58, end: 0.63 }], messageC_opacity_out: [1, 0, { start: 0.85, end: 0.9 }], pinB_scaleY: [0.5, 1, { start: 0.5, end: 0.55 }], pinC_scaleY: [0.5, 1, { start: 0.72, end: 0.77 }], pinB_opacity_in: [0, 1, { start: 0.5, end: 0.55 }], pinC_opacity_in: [0, 1, { start: 0.72, end: 0.77 }], pinB_opacity_out: [1, 0, { start: 0.58, end: 0.63 }], pinC_opacity_out: [1, 0, { start: 0.85, end: 0.9 }], },
-
해결됨애플 웹사이트 인터랙션 클론!
비디오에 맞춰 텍스트가 나타나는 타이밍 질문입니다
안녕하세요 선생님 강의 잘 듣고있습니다!! 다름이 아니라 스크롤 섹션 2의 경우에는 비디오 타이밍에 맞춰 텍스트가 나오는데 그 타이밍 설정은 직접 콘솔로 scrollRatio 값을 확인해가면서 정하면 되는걸까요??
-
해결됨애플 웹사이트 인터랙션 클론!
스크롤 이벤트 시작하는 위치 질문입니다
안녕하세요 선생님 강의 잘 듣고 있습니다. 다름이 아니라 스크롤 이벤트를 시작하는 위치 구하는 부분에서 이벤트가 실행될 scroll-section의 offsetTop으로 구하는 것과 이벤트가 실행될 scroll-section 위에 존재하는 scroll-section들의 높이 합으로 구하는 것이 차이가 있는지 궁금합니다.