월 15,400원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결애플 웹사이트 인터랙션 클론!
캔버스관련 질문있습니다.
캔버스에 인터랙션을 적용할 때 RAF의 실행속도가 너무 빨라 인식이 가끔 안먹히는 문제를 보통 어떻게 해결하시나요??
- 미해결애플 웹사이트 인터랙션 클론!
선생님 아래와 같은 사이트는 어떤 식으로 구현된 걸까요?
https://trends.cmiscm.com/ 이런 사이트는 어떻게 구현을 하는지 알 수 있을까요? 색상 추출을 하고나서 어떤식으로 드로잉 하는지 궁금합니다.
- 미해결애플 웹사이트 인터랙션 클론!
저작권 관련 질문 있습니다
배운것 토대로 디자인 다르게 하고 응용을 통하여 수입 목적으로 홈페이지를 만들경우 애플쪽 저작권에 걸리나요? 특히 애플 같이 스크롤에 반응하는 인터랙션 경우도 걸리나요.? css 같은 경우에는 저작권이 있다 라고 들어서 궁금해서 여쭈어 봅니다
- 미해결애플 웹사이트 인터랙션 클론!
위치 이동에 관해 질문이 있습니다.
JS보다 어려운게 CSS 같습니다... 질문이 있는데, 선생님께서는 위치를 이동시킬 때, 1. margin-Top과 2. position을 주고 top을 주는 두 방법에 대해 어떤 기준으로 나눠서 사용하시는지 궁금합니다. 아니면, 그냥 사용하시는건지요
- 미해결애플 웹사이트 인터랙션 클론!
2560x1440 모니터 비율 질문드립니다!
화면처럼 레이아웃이 깨져보여서 .. 이건 어떻게 오류를 잡아야할까요?
- 미해결애플 웹사이트 인터랙션 클론!
마지막 에러 해결 방법이 정말 궁금합니다.
코드 상과 타이밍 상을 생각해도 yOffset을 쓰는 것에 논리적인 모순이 없는 것 같은데에도 불구하고, 캔버스 중간에 중간 이미지가 그려지는 이상이 생기는 이유가 궁금합니다. console을 찍어도 순간 포착되는 이미지가 찍히지 않는데도 캔버스에 등장하는 이유를 알려주세요ㅠㅠ 혹시 yOffset이 delayedYOffset보다 큰 관계로 enterNewScene이 제대로 작동하지 않아서 그런걸까요?
- 미해결애플 웹사이트 인터랙션 클론!
scroll 시 loop 호출에 관련해서 질문있습니다.
if(!rafState) { //현재 스크롤 상태가 아닌 경우에 스크롤 한 경우 loop 함수 RAF로 실행 rafId = requestAnimationFrame(loop); rafState = true; } 이 부분에서 loop를 그냥 호출해도 되지 않나요? 굳이 RAF로 호출해주신 이유가 궁금합니다. 또한 rafId를 저장한 이유가 궁금합니다. 여기서 저장하지 않아도 loop 함수 내부에서 RAF 실행으로 반환된 rafId로 인해 cancel되지 않나요? 여기서 실행한 rafId로 실행이 종료되진 않을 것 같은데 굳이 저장해주신 이유가 궁금합니다.
- 미해결애플 웹사이트 인터랙션 클론!
스크롤 비율에 따른 효과
스크롤 비율에 따라 해당 태그에 클래스명을 붙혔다 제거하는 방식을 안 쓰는 이유를 알 수 있을까요??
- 미해결애플 웹사이트 인터랙션 클론!
3번째 섹션의 캔버스 비율 관련 질문입니다.
혹시 캔버스 비율 관련 코드를 playAnimation가 아닌 resize 이벤트의 바인딩으로 옮길 순 없나요?
- 미해결애플 웹사이트 인터랙션 클론!
블렌딩이 뭔가요?
블렌딩 처리를 한다는데 용어의 뜻을 잘 모르겠습니다! 또한, 리사이징시 스크롤 이벤트가 발생하지 않았는데도, 캔버스가 반응형으로 늘어나는 이유 또한 궁금합니다.
- 미해결애플 웹사이트 인터랙션 클론!
코드 분석에 있어서 질문이 있습니다.
혹시 개발자 도구를 통해서는 JS 파일을 확인할 방법이 없을까요? source 탭에 들어가면 파일이 있는 것은 확인했는데, 다 빌드된 상태인 것 같아서 awwards 사이트와 같은 페이지의 JS 코드를 파악하는게 저같은 초보입장에서는 어렵습니다 ㅠㅠㅠㅠ 혹시 팁같은 것이 있을까요?
- 미해결애플 웹사이트 인터랙션 클론!
화면에 고정된 경우 질문있습니다.
sticky와 fixed의 경우, 차이점은 알겠는데, 사용을 어떻게 구분지어야할지에 대한 질문이 있습니다. fixed의 경우는 브라우저창에 고정된 것이고, sticky는 position 값이 있는 부모 컨테이너의 특정 좌표에 고정된 것이라고 이해해도 될까요? 또, 인터랙션 시 화면에 계속 고정되어야하는 경우는 fixed, 특정 구간에서만 고정되게 하려면 sticky를 쓰는게 좋을까요?
- 미해결애플 웹사이트 인터랙션 클론!
공부 관련 질문있습니다!
선생님께서는 이러한 인터랙션을 구현하거나 공부할 때 참고하시는 사이트나 다른 방법들이 있을까요?
- 해결됨애플 웹사이트 인터랙션 클론!
스크롤애니메이션이 최초로 한번만 동작해요 ㅜ
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 0번째 scene에서 최초로 스크롤을 했을때는 opacity애니메이션이 잘 동작해요! 다만 0번째 scene을 한번이라도 벗어나면 애니메이션이 없어져버리고 opacity가 변하질않아요 .. 또는 2번째 scene에서 새로고침해서 스크롤을 올렸을 경우에도 messageA_opacity_in값이 출력이안돼요 콘솔로 this.messageA_opacity_in 체크해봤을 때 다른scene넘어가고나면 값을 출력하지않고있긴하더라고요. 딱히 에러가 발생하고있지도 않습니다. 스크롤함수인데 동작을 따로 안하는 이유가 있을까요? 링크 첨부드립니다 https://codesandbox.io/s/fervent-phoebe-pdkdfq?file=/src/App.vue
- 미해결애플 웹사이트 인터랙션 클론!
scrollLoop 함수에서 스크롤 증가 if문
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. scrollLoop 함수에서 스크롤 증가 if문 작성중인데, 이해가 잘 가지 않는 부분이 있습니다. if(YOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) { currentScene++; } 이 부분으로 sceneInfo[currentScene].scrollHeight 씬인포 배열안에 currentScene이 들어가는 걸까요?
- 해결됨애플 웹사이트 인터랙션 클론!
선생님 궁금해요 !!!!
https://codesandbox.io/s/fervent-phoebe-pdkdfq?file=/src/App.vue 선생님!!! 스크롤을 내리거나 올릴때 체크가 잘되다가 마지막 scene만 가면 오류가나요 .. 마지막 scene이 끝날때 0,1,2 에서 끝나야하는데 3이 찍히는것도 좀 이상한거같구요. 마지막 scene지점에서 에러가나서 끝지점에서 다시 올라가는게 안돼요 ㅠ for문 부터 잘못된걸까요 ㅠ아님 height값 체크가 잘못되고있는걸까요 ㅠ
- 미해결애플 웹사이트 인터랙션 클론!
이런 질문도 답변 가능할까요?
지금 16강 수강중이구요 스크롤 애니메이션을 vue에서 따라 작성해보고있는데 .. 값이 다르게 찍히는게 있어서 혹시나 해결방법을 아시는지 궁금해서 여쭤봅니다. ㅠㅠ 다른 부분들은 나름 어거지로??어떻게 따라와서 텍스트 하나를 나타나고 사라지게 하는게 되긴하는데 ... 추후 강의를 똑같이 따라갈 수 있을런지... 제 이슈는 이렇습니다. value값이 동일하게 찍히질않습니다. 강의에서는 아래처럼 values.length값이 배열로 잘 나오는데.. 저는 value값을 콘솔로 찍었을 때는 이렇게나오고 values[0]은 undefined로 나와버려서 length값 체크도못하고 calcValue함수 내에서도 이런식으로 작성하면서 따라했거든요 .. object name을 콕 찝어서 데려와야만 배열값을 얻어요.. 배열로얻어오지못하니 강의와 똑같이 구현해보려고 calcvalue함수를 copy해서 하나는 in 하나는 out으로하는 무지막지한 코드를 작성해놨어요🤯🤯 같은 자바스크립트인데 환경좀 다르다고 이렇게 value값이 다를일인가요........... value의 배열값을 강의처럼 얻을수없다면 제 vue 스크롤연습은 여기서 접어야하는걸까요..?........ 이후 강의는 따라해볼 엄두가 안나네요 ..ㅜ 이틀 삽질하다 슬퍼서 질문올려봅니다... full code남기며 ... 질문마무리하겠습니다🥺 <template> <div class="main" ref="scrollWrap"> <section class="scroll-section" id="scroll-section-0" ref="scrollSection0"> <div class="hero" :style="`background-image: url(${이미지})`"> <div class="text-test">소환사 여러분</div> <div class="sticky-elem hero-message a" ref="message1"> <div class="year">10th ANNIVERSARY</div> </div> <!-- <div class="sticky-elem hero-message b" ref="message2"> <div class="thanks">THANK YOU SUMMONERS</div> </div> <div class="sticky-elem hero-message c" ref="message3"> <div class="greeting">리그오브레전드와 10년간 함께 해주신 소환사 여러분, 감사합니다!</div> <div class="greeting">THANK YOU, SUMMONERS!</div> </div> --> </div> </section> <section class="scroll-section" id="scroll-section-1" ref="scrollSection1"> <p> Lorem, ipsum dolor sit amet consectetur adipisicing elit. Assumenda<br> </p> </section> <button @click="setLayout">레이아웃d</button> <div> <input type="text" @input="setSearch($event.target.value)" /> <button @click="searchForPlayer(e)">serach</button> <div v-if="playerData" class="box"> <p>{{playerData.name}}</p> <p>{{playerData.summonerLevel}}</p> <p>{{playerData.profileIconId}}</p> <p><img width="100" height="100" :src="`http://ddragon.leagueoflegends.com/cdn/12.7.1/img/profileicon/${playerData.profileIconId}.png`"></p> </div> <div v-else class="box2"> <p>{{noPlayer}}</p> <p>데이터가없습니다</p> </div> </div> <router-view></router-view> <router-link to="/JH">링크</router-link> </div> </template> <script> import { onMounted, ref} from 'vue' import axios from 'axios' //씬을나눈다 //씬의 높이를 세팅한다 //활성화 시킬 씬을 결정한다 //this.prevScrollHeight = this.prevScrollHeight + this.sceneInfo[i].scrollHeight; //얘네는같은거임 this.prevScrollHeight += this.sceneInfo[i].scrollHeight; export default { name: 'App', components: { }, data() { return { errorText: "존재하지않는 데이터입니다", 이미지: "https://universe.leagueoflegends.com/images/championsBackground.jpg", sceneInfo: [ { type: 'sticky', heightNum: 5, scrollHeight: 0, objs: { // container: document.querySelector('scroll-section-0') container : this.$refs.scrollSection0, massageA : this.$refs.message1, massageB : this.$refs.message2, massageC : this.$refs.message3, }, values: { messageA_opacity_in: [0,1, {start: 0.1, end: 0.2}], messageA_opacity_out: [1,0, {start: 0.25, end: 0.3}], } }, { type: 'normal', heightNum: 5, scrollHeight: 0, objs: { container: this.$refs.scrollSection1 } }, ], //window.pageYOffset 갱신 yOffset : 0, //현재 스크롤 위치(yOffset)보다 이전에 위치한 스크롤 섹션들의 스크롤 높이값의 합 prevScrollHeight : 0, //현재 활성화된 scene currentScene : 0, totalScrollHeight: 0, messageA_opacity_in: '', messageA_opacity_out: '', currentYOffset: 0, rv: 0, scrollRatio: 0, outerScrollRatio:0, enterNewScene: false, scrollHeight: 0, partSCrollStart: 0, partScrollEnd: 0, partScrollHeight: 0, currentSceneValues: '', infoCurretScene: 0, } }, setup() { let searchText = ref(""); let playerData = ref(); let noPlayer = ref(); let deleteMessage = ("존재하지않음"); let playerId = ref([]); let apiKey = "RGAPI-62f8907f-eab8-4333-bc43-94d32a09be28"; const setSearch = (검색어) => { searchText = 검색어 } const searchForPlayer = () => { playerData.value = null noPlayer.value = null //Handle the API call axios.get(`https://kr.api.riotgames.com/lol/summoner/v4/summoners/by-name/${searchText}?api_key=${apiKey}`) .then((response) => { playerData.value = response.data }).catch(error => { playerData.value = null noPlayer.value = deleteMessage; }); } return { searchText, playerData, playerId, setSearch, searchForPlayer, }; }, methods: { init() { this.setLayout(); this.scrollLoop(); }, setLayout() { // Sticky Conainer 의 높이를 설정함. // for( let i = 0; i < this.sceneInfo.length; i++ ){ // this.sceneInfo[i].scrollHeight = this.sceneInfo[i].heightNum + window.innerHeight; // this.sceneInfo[i].objs.container.style.height = `${this.sceneInfo[i].scrollHeight}px`; // } this.sceneInfo[0].scrollHeight = this.sceneInfo[0].heightNum + window.innerHeight; this.$refs.scrollSection0.style.height = `${this.sceneInfo[0].scrollHeight}px`; this.sceneInfo[1].scrollHeight = this.sceneInfo[1].heightNum + window.innerHeight; this.$refs.scrollSection1.style.height = `${this.sceneInfo[1].scrollHeight}px`; this.yOffset = window.pageYOffset; this.totalScrollHeight = 0; for( let i = 0; i < this.sceneInfo.length; i++ ){ this.totalScrollHeight += this.sceneInfo[i].scrollHeight; if(this.totalScrollHeight >= this.yOffset){ this.currentScene = i; break; } } }, calcValues(values, currentYOffset){ this.rv; this.scrollHeight = this.sceneInfo[this.currentScene].scrollHeight; //현재 scene에서 스크롤된 범위를 비율로 구하기 this.scrollRatio = this.currentYOffset / this.scrollHeight; this.outerScrollRatio = (this.yOffset - this.prevScrollHeight) / this.scrollHeight; this.infoCurretScene = this.sceneInfo[this.currentScene]; this.currentSceneValues = this.sceneInfo[this.currentScene].values; if(this.currentSceneValues.messageA_opacity_in.length === 3){ //start ~ end 사이에 애니메이션 실행 this.partSCrollStart = this.currentSceneValues.messageA_opacity_in[2].start * this.scrollHeight; this.partScrollEnd = this.currentSceneValues.messageA_opacity_in[2].end * this.scrollHeight; this.partScrollHeight = this.partScrollEnd - this.partSCrollStart; if(this.currentYOffset >= this.partSCrollStart && this.currentYOffset <= this.partScrollEnd){ this.rv = (this.currentYOffset - this.partSCrollStart) / this.partScrollHeight * ( this.currentSceneValues.messageA_opacity_in[1] - this.currentSceneValues.messageA_opacity_in[0]) + this.currentSceneValues.messageA_opacity_in[0]; }else if(this.currentYOffset < this.partSCrollStart){ this.rv = this.currentSceneValues.messageA_opacity_in[0]; }else if(this.currentYOffset > this.partScrollEnd){ this.rv = this.currentSceneValues.messageA_opacity_in[1]; } }else { this.rv = this.scrollRatio * ( this.currentSceneValues.messageA_opacity_in[1] - this.currentSceneValues.messageA_opacity_in[0]) + this.currentSceneValues.messageA_opacity_in[0]; } return this.rv; }, calcValues2(values, currentYOffset){ this.rv; this.scrollHeight = this.sceneInfo[this.currentScene].scrollHeight; //현재 scene에서 스크롤된 범위를 비율로 구하기 this.scrollRatio = this.currentYOffset / this.scrollHeight; this.outerScrollRatio = (this.yOffset - this.prevScrollHeight) / this.scrollHeight; this.infoCurretScene = this.sceneInfo[this.currentScene]; this.currentSceneValues = this.sceneInfo[this.currentScene].values; if(this.currentSceneValues.messageA_opacity_out.length === 3){ //start ~ end 사이에 애니메이션 실행 this.partSCrollStart = this.currentSceneValues.messageA_opacity_out[2].start * this.scrollHeight; this.partScrollEnd = this.currentSceneValues.messageA_opacity_out[2].end * this.scrollHeight; this.partScrollHeight = this.partScrollEnd - this.partSCrollStart; if(this.currentYOffset >= this.partSCrollStart && this.currentYOffset <= this.partScrollEnd){ this.rv = (this.currentYOffset - this.partSCrollStart) / this.partScrollHeight * ( this.currentSceneValues.messageA_opacity_out[1] - this.currentSceneValues.messageA_opacity_out[0]) + this.currentSceneValues.messageA_opacity_out[0]; }else if(this.currentYOffset < this.partSCrollStart){ this.rv = this.currentSceneValues.messageA_opacity_out[0]; }else if(this.currentYOffset > this.partScrollEnd){ this.rv = this.currentSceneValues.messageA_opacity_out[1]; } }else { this.rv = this.scrollRatio * ( this.currentSceneValues.messageA_opacity_out[1] - this.currentSceneValues.messageA_opacity_out[0]) + this.currentSceneValues.messageA_opacity_out[0]; } return this.rv; }, playAnimation(){ // const values = this.sceneInfo[this.currentScene].objs; // const values = this.sceneInfo[this.currentScene].values; this.currentYOffset = this.yOffset - this.prevScrollHeight; console.log(this.currentSceneValues[0]); switch (this.currentScene) { case 0: this.messageA_opacity_in = this.calcValues(this.sceneInfo[0].values.messageA_opacity_in, this.currentYOffset); this.messageA_opacity_out = this.calcValues2(this.sceneInfo[0].values.messageA_opacity_out, this.currentYOffset); if(this.outerScrollRatio <= 0.22){ //in this.$refs.message1.style.opacity = this.messageA_opacity_in; } else { //out this.$refs.message1.style.opacity = this.messageA_opacity_out; } break; case 1: break; } }, scrollLoop(){ this.enterNewScene = false; //scrollHeight값 누적되지않도록 0으로 this.prevScrollHeight = 0; for(let i = 0; i< this.currentScene; i++){ this.prevScrollHeight += this.sceneInfo[i].scrollHeight; } if(this.yOffset > this.prevScrollHeight + this.sceneInfo[this.currentScene].scrollHeight){ this.enterNewScene = true; this.currentScene++; // this.$refs.scrollWrap.setAttribute('id', `show-scene-${this.currentScene}`); } if(this.yOffset < this.prevScrollHeight){ if(this.currentScene === 0) return; this.enterNewScene = true; this.currentScene--; // this.$refs.scrollWrap.setAttribute('id', `show-scene-${this.currentScene}`); } this.$refs.scrollWrap.setAttribute('id', `show-scene-${this.currentScene}`); if(this.enterNewScene) return; this.playAnimation(); }, }, mounted() { this.init(); window.addEventListener('DOMContentLoaded',this.setLayout); window.addEventListener('resize', this.setLayout); window.addEventListener('scroll', () =>{ //스크롤이 일어날 때 yOffset 의 값을 window.pageYOffset 값으로 갱신 this.yOffset = window.pageYOffset; this.scrollLoop(); }); }, } </script> <style lang="scss"> body { margin: 0; } .scroll-section { border: 1px solid red } .text-test { position: relative; padding: 20px; z-index: 1; color: red; } </style>
- 미해결애플 웹사이트 인터랙션 클론!
em , rem 차이
안녕하세요 강의 정말 잘보고있습니다! 선생님꼐서 padding 에 rem 단위를 쓸떄도 있고 em 단위로 쓸때가 있으신데 제가 알기로는 rem 은 root 단위를 em 은 현재 폰트의 단위에 따른 단위로 알고있는데 선생님께서는 어떤 기준으로 rem과 em 을 구분해서 padding 값이나 margin 값에 사용하시는지 궁금합니다!
- 해결됨애플 웹사이트 인터랙션 클론!
scroll-section 1 에서 2로 넘어가는 부분(section 2의 시작점)에서,,
안녕하세요. main-add.js에서 코드 붙여넣고 애니메이션은 정상적으로 작동하는데요. 한 가지 신경 쓰이는 이슈가 발생했는데 원인을 모르겠습니다. scroll-section 1 에서 2로 넘어가는 부분(normal scroll 끝나는 부분, section 2의 시작점)에서 섹션2의 desc-message b, desc-message c (pin 메시지 부분)가 처음 한번만 (새로고침 하고 스크롤 할 때) 빠르게 나왔다가 사라집니다. 너무 찰나의 순간이라서 스크린샷도 찍을 수가 없어요. js 코드가 좀 어수선해보여서 정리 후 다시 업로드 하겠습니다;; * 자바스크립트 포맷 정리하고 css 추가 업로드 하였습니다. +추가로 prettier 같은 포매터 사용 때문에 강사님이 작성하시는 것보다 js의 가독성이 떨어지는 것 같은데 강사님은 따로 포매터 사용은 안하시나요?
- 미해결애플 웹사이트 인터랙션 클론!
50vh
.scroll-section { position: relative; padding-top: 50vh; } 위와 같이 padding-top에 50vh를 넣고 setLayout함수에서 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.content.offsetHeight + window.innerHeight * 0.5; } sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`; sticky인 경우 innerHeight의 배수만큼만, normal인 경우 실제 높이에 50vh 만큼을 더해주고 있습니다. 질문1: 어짜피 setLayout에서 높이를 재설정하고 있는데 css에서 패딩을 주는 이유가 무엇일까요? 질문2: sticky와 normal에 50vh의 보정이 서로 다르게 들어가는 이유는 무엇일까요? 감사합니다~~