프론트엔드/Javascript 연습과제 해답

자바스크립트 문법 5 - 이벤트(Event) 처리하기 3 해답

syleemomo 2022. 1. 15. 11:56
728x90

 

* 연습과제 1~3 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>자바스크립트 연습</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <div id='section'>
        <div class="contents up">
            <img src="people-1.jpg"/>
        </div>
        <div class="contents down">
            <img src="people-2.jpg"/>
        </div>
        <div class="contents left">
            <img src="people-3.jpg"/>
        </div>
        <div class="contents right">
            <img src="people-4.jpg"/>
        </div>
    </div>
    <script src="app.js"></script>
</body>

</html>

보여주고자 하는 4개의 사진이 필요함

body{
    height: 200vh;
}
#section{
    display: flex;
    justify-content: center;
    align-items: center;
    margin-top: 400px;
}
.contents{
    background-color: brown;
    width: 300px;
    height: 300px;
    text-align: center;
    line-height: center;
    margin-right: 10px;
    opacity: 0;
    transition: all .5s ease;
    overflow: hidden;
}
.contents img{
    width: 100%;
    height: 100%;
}
.up{
    /* y 좌표를 아래쪽으로 100px 만큼 내린다 */
    transform: translate(0, 100px); 
}
.down{
    /* y 좌표를 위쪽으로 100px 만큼 올린다 */
    transform: translate(0, -100px);
}
/* x 좌표를 오른쪽으로 100px 만큼 이동한다 */
.left {
    transform: translate(100px, 0);
}
/* x 좌표를 왼쪽으로 100px 만큼 이동한다 */
.right {
    transform: translate(-100px, 0);
}
.show{
    opacity: 1;
    transform: none;
}
const contents = document.querySelectorAll('.contents')

function startAnimation() {
    for (let content of contents) {
        console.log(content.getBoundingClientRect().top)
        if (!content.classList.contains('show') && content.getBoundingClientRect().top < 200) {
            content.classList.add('show')
        }
    }
}

window.addEventListener('scroll', startAnimation)

 

* 연습과제 4

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main>
    <section><p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Magnam veniam alias animi expedita adipisci earum veritatis vel molestiae magni et ipsa pariatur, mollitia, quis necessitatibus possimus praesentium ea culpa ut?</p></section>
    <section><p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium officiis itaque vero, incidunt, provident saepe laboriosam id, voluptatum vitae quia doloremque eaque sit consectetur? At voluptate cum vero vitae corporis!
    Tenetur facere fugiat deserunt earum sed, autem dicta rerum adipisci, perspiciatis quam laboriosam accusantium qui error architecto temporibus. Odit ipsam fugiat nisi autem perferendis esse eaque nulla voluptatem excepturi quidem.</p></section>
    <section><p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quasi, laudantium veritatis dignissimos magni ipsam nulla, asperiores libero recusandae fugit, soluta maxime corporis autem tempora harum. Veritatis neque id explicabo excepturi?</p></section>
    <section><p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Minima quae consectetur, sapiente ducimus saepe tempora nihil ab aut dolorum iusto aliquam. Alias laboriosam hic accusantium perspiciatis corporis, commodi tenetur error?</p></section>
    <section><p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Excepturi velit odio iusto sed rem, ex eligendi quod, laudantium optio, illo ipsam! In nam blanditiis error, nulla a tempora praesentium at.
    Ex provident facilis perferendis deserunt laudantium ipsam debitis odit. Inventore aut, quisquam impedit nesciunt officiis omnis qui provident, veniam recusandae odit ullam voluptatem quaerat, maiores sunt tenetur! Rerum, labore. At!</p></section>
  </main>
  <script src="app.js"></script>
</body>
</html>
body{
  margin: 0; padding: 0;
  box-sizing: border-box;
}
main{
  width: 100%;
  perspective: 5000px; /* 원근감을 줘서 슬라이드가 플립되도록 함 */
}
main section{
  width: 100%;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  position: fixed; left: 0; right: 0; 
  top: 50%; transform: translateY(0%);
  transition: 1s ease-in-out;
}
main section p{
  font-size: 2rem;
  font-weight: bold;
  color: seashell;
  text-align: center;
  margin: 0; padding: 1rem 2rem;
  /* border: 1px solid red; */
}
main section:nth-child(1){
  background-color: yellowgreen;
}
main section:nth-child(2){
  background-color: brown;
  opacity: 0;
}
main section:nth-child(3){
  background-color: yellow;
  opacity: 0;
}
main section:nth-child(4){
  background-color: springgreen;
  opacity: 0;
}
main section:nth-child(5){
  background-color: slateblue;
  opacity: 0;
}
const main = document.querySelector('main')
const sections = main.querySelectorAll('section')
const clientHeight = document.documentElement.clientHeight // 브라우저 높이
const scrollHeight = Math.max(
  document.body.scrollHeight, document.documentElement.scrollHeight,
  document.body.offsetHeight, document.documentElement.offsetHeight,
  document.body.clientHeight, document.documentElement.clientHeight
)
const scrollRange = scrollHeight - clientHeight // 세로방향 스크롤 범위
const scrollRangeOfOneSection = scrollRange / (sections.length - 1) // 하나의 섹션에 대한 스크롤 범위
const speedOfSlide = 0.2
let index = 0, timer, angle = 0

function trotthling(handler, e){   // 100ms 동안 이벤트 금지하기 (자연스러운 슬라이드 효과 연출)
  if(!timer){
    timer = setTimeout(function(){
      handler(e)
      timer = null 
    }, 100)
  }
}

function initializeStyle(sections){      // 섹션의 기존스타일 초기화 (다른 슬라이드 숨기기)
  for(let i=0; i<sections.length; i++){ 
    const section = sections[i]
    section.style.opacity = '0'
    section.style.transform = 'translateY(0%)'
    setTimeout(function(){ // 부드러운 화면 전환을 위한 타이머 적용
      section.style.transition = 'none'
    }, 100)
  }
}

function changeSlide(e){
  console.log('scroll', e.deltaY)
  
  if(e.deltaY > 0){ // 스크롤을 내린 경우
    angle += parseInt(e.deltaY * speedOfSlide) // 스크롤을 내린 거리의 speedOfSlide 비율만큼만 angle 값 증가
    console.log(angle)

    if(angle > 360){ // 다음 슬라이드의 flip 을 위한 angle 초기화 
      angle = 0
      initializeStyle(sections) // angle 이 360도에서 0도로 갑자기 변할때 트랜지션이 적용되어 있어서 슬라이드가 빠르게 돌면서 로테이션되는데 이를 방지하고자 트랜지션을 제거함
    }

    if(Math.abs(angle - 90) < 30){ // 90도 +-10도 근처에서 코드블럭 실행
      angle += 180 // 다음 슬라이드의 위상은 270도부터 시작해야 하므로 180도 증가 (슬라이드 flip)
      index++ // 다음 슬라이드 선택을 위한 인덱스값 증가 
      if(index > sections.length - 1){
        index = 0
      }
      setTimeout(function(){ // 부드러운 화면 전환을 위한 타이머 설정후 초기화 적용하기
        initializeStyle(sections)
      }, 100)
    }
    console.log(index)
    
    // agnle 이 270보다 커질때 화면전환하면 화면이 뚝뚝 끊기면서 갑자기 바뀌므로 40도 정도 여유를 두고 부드럽게 전환되도록 함
    const section = sections[index]
    if(angle > 270 || angle > 0){   // angle 이 360도에서 0도로 바뀌고 나서 다시 부드럽게 회전할 수 있도록 트랜지션 적용함
      setTimeout(function(){        // 슬라이드 flip 후 자연스러운 슬라이드 움직임을 위한 트랜지션 재설정
        section.style.transition = '1s ease-in-out' 
      }, 100)
    } 
    section.style.opacity = '1'
    section.style.transform = `translateY(0%) rotateX(${parseInt(angle)}deg)`
  }
}

document.addEventListener('wheel', (e) => trotthling(changeSlide, e))

 

* 연습과제 5

const fileInput = document.getElementById('file-input')
const imgBox = document.getElementById('img-box')

function isValid(type){
    return type.split('/')[0] === 'image'
}

function displayImg(src){
    imgBox.innerHTML = `<img src='${src}'/>`
}

function rememberImg(e){
    console.log(e.target.result) // reader 객체로 읽어온 데이터  (이미지 경로)
    localStorage.setItem('file', JSON.stringify(e.target.result))
}

function uploadImg(e){
    const file = e.target.files[0]
    const reader = new FileReader() // 사용자가 업로로드한 파일 데이터를 읽어오기 위한 파일 객체

    if(!isValid(file.type)){
        imgBox.innerHTML = 'File type is not valid !'
        return;
    }

    const src = URL.createObjectURL(file)
    displayImg(src) // 화면에 이미지를 보여주기

    reader.onload = rememberImg // 파일 읽기가 끝나면 rememberImg 를 실행함
    reader.readAsDataURL(file) // reader 객체가 파일을 읽어오기
}

// 화면이 처음 로딩될때 로컬스토리지에 저장된 이미지를 보여주기
function renderImg(){
    const fileStored = JSON.parse(localStorage.getItem('file'))
    if(fileStored){
        displayImg(fileStored)
    }
}

fileInput.addEventListener('change', uploadImg)
window.addEventListener('load', renderImg)

위 코드에서 localStorage 가 들어간 부분을 sessionStorage 로 변경하면 된다. 

 

* 연습과제 6

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>자바스크립트 연습</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <input id="file-input" type="file" multiple>
    <div id='img-box'></div>

    <script src='app.js'></script>
</body>
</html>
body{
    margin: 0;
    padding: 0;
}
#file-input{
    position: absolute;
    left: 50%;
    top: 100px;
    transform: translate(-50%);
}
#img-box{
    width: 80%;
    overflow: hidden;

    position: absolute;
    left: 50%;
    top: 200px;
    transform: translate(-50%);

    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
}
.img-item{
    width: 200px;
    height: 200px;
    overflow: hidden;
}
.img-item img{
    width: 100%;
    height: 100%;
}
const fileInput = document.getElementById('file-input')
const imgBox = document.getElementById('img-box')

function isValid(type){
    return type.split('/')[0] === 'image'
}

function displayImg(src){
    console.log(src.split(':')[0])
    const srcPrefix = src.split(':')[0]
    if(srcPrefix === 'blob' ){
        imgBox.innerHTML += `<div class='img-item'><img src='${src}'/></div>`
    }else{
        imgBox.innerHTML += `<div class='img-item'><img src=${src}/></div>`
    }
    
}
function upload(file){
    const reader = new FileReader() // 사용자가 업로로드한 파일 데이터를 읽어오기 위한 파일 객체

    if(!isValid(file.type)){
        alert('File type is not valid !')
        return;
    }

    const src = URL.createObjectURL(file)
    displayImg(src) // 화면에 이미지를 보여주기

    reader.onload = rememberImg // 파일 읽기가 끝나면 rememberImg 를 실행함
    reader.readAsDataURL(file) // reader 객체가 파일을 읽어오기
}

function rememberImg(e){
    console.log(e.target.result) // reader 객체로 읽어온 데이터  (이미지 경로)
    const files = JSON.parse(localStorage.getItem('files')) || []
    if(files){
        const file = JSON.stringify(e.target.result)
        files.push(file)
    }
    localStorage.setItem('files', JSON.stringify(files))
}

function uploadImg(e){
    const files = e.target.files
    for(let file of files){
        upload(file)
    }
}

// 화면이 처음 로딩될때 로컬스토리지에 저장된 이미지를 보여주기
function renderImg(){
    imgBox.innerHTML = ''
    const files = JSON.parse(localStorage.getItem('files')) || []
  
    if(files.length > 0){
        for(let file of files){
            displayImg(file)
        }
    }
}

fileInput.addEventListener('change', uploadImg)
window.addEventListener('load', renderImg)

 

* 연습과제 7

<!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <pre>      인생은 롤러코스터와 같아.

      둘다 오르막과 내리막이 있으니깐
      
      하지만 두려움에 떨거나 즐기는 것은
      
      너의 선택이야
      </pre>
    <script src="app.js"></script>
  </body>
  </html>
body{
  margin: 0; padding: 0;
  background-color: #0e1111;
  display: flex;
  justify-content: center;
  align-items: center;
  overflow: hidden;
}
body pre{
  color: green;
  font-size: 1.2rem;
}
.letter{
  position: absolute;
  top: 0;
  color: green;
  transition: 20ms ease-in-out;
  animation: zoom 1s ease-in-out forwards;
}
@keyframes zoom{
  0%{
    transform: scale(1);
    opacity: 1;
  }
  50%{
    transform: scale(1.3);
    opacity: 0.3;
  }
  100%{
    transform: scale(1);
    opacity: 0.1;
  }
}
const paragraph = document.querySelector('pre')
const text = paragraph.innerText
const scrollbarWidth = 50
const maxFontSize = 30
const widthOfBrowser = document.documentElement.clientWidth - (scrollbarWidth + maxFontSize) // 브라우저 높이 - (스크롤바 너비 + 글자너비)
let isStarted = false // 스크롤시 애니메이션을 한번만 실행하는 플래그 변수 
let timer

function pickRandomNumber(n){
  return Math.floor(Math.random() * n)
}

function pickRandomLetter(text){ // 문자열에서 랜덤글자를 선택하는 함수
  const randomIndex = pickRandomNumber(text.length)
  return text[randomIndex]
}
function createLetter(text, left, top){ // 문자열, 위치(x, y) 좌표를 입력으로 받아서 글자를 생성하는 함수
  console.log('creating letter...')
  const span = document.createElement('span')
  span.className = 'letter'
  span.style.left = left + 'px'
  span.style.fontSize = pickRandomNumber(maxFontSize) + 'px' // 최대 폰트크기보다 작은 글자 생성
  span.innerText = pickRandomLetter(text) 
  if(top) span.style.top = top + 'px' 
  return span 
}

function displayLetter(){ // 랜덤글자를 생성하고 화면에 보여주기 위한 타이머 설정
  if(timer) clearTimeout(timer)

  // const randomLetter = pickRandomLetter(text) 
  const letter = createLetter(text , pickRandomNumber(widthOfBrowser)) // 랜덤한 위치에 랜덤한 글자 생성 (브라우저 너비를 벗어나지 않는 범위에서)
  document.body.appendChild(letter) // 화면에 랜덤글자 디스플레이 
  setInterval(function(){ // 생성한 랜덤글자를 Y축으로 이동시키기 위한 타이머 설정 
    letter.style.top = letter.offsetTop + 10 + 'px' // 생성한 랜덤글자를 아래방향으로 이동
    const cloneLetter = createLetter(text, letter.offsetLeft, letter.offsetTop) // 처음에 생성한 랜덤글자와 동일한 글자를 현재 Y측 위치에 복제
    document.body.appendChild(cloneLetter) // 복제된 랜덤글자를 화면에 디스플레이 
  }, 30)
  timer = setTimeout(displayLetter, 100)
}

function startTextAnimation(){
  if(!isStarted){
    paragraph.innerText = '' // 브라우저 화면 초기화 
    setTimeout(displayLetter, 100)

    isStarted = true 
  }
}

window.addEventListener('wheel', startTextAnimation)

 

* 연습과제 8

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main>
    <section><p>1</p></section>
    <section><p>2</p></section>
    <section><p>3</p></section>
    <section><p>4</p></section>
    <section><p>5</p></section>
  </main>
  <script src="app.js"></script>
</body>
</html>
body{
  margin: 0; padding: 0;
  box-sizing: border-box;
  overflow: hidden;
}
main{
  width: 100vw; height: 100vh;
  transition: 1s ease-in-out;
  display: flex;
  justify-content: flex-start;
}
main section{
  width: 100%; height: 100%;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
}
main section p{
  font-size: 2rem;
  font-weight: bold;
  color: seashell;
  text-align: center;
  margin: 0; padding: 1rem 2rem;
}
main section:nth-child(1){
  background-color: yellowgreen;
}
main section:nth-child(2){
  background-color: brown;
}
main section:nth-child(3){
  background-color: yellow;
}
main section:nth-child(4){
  background-color: springgreen;
}
main section:nth-child(5){
  background-color: slateblue;
}
const main = document.querySelector('main')
const sections = main.querySelectorAll('section')
const clientHeight = document.documentElement.clientHeight // 브라우저 높이
const scrollHeight = Math.max(
  document.body.scrollHeight, document.documentElement.scrollHeight,
  document.body.offsetHeight, document.documentElement.offsetHeight,
  document.body.clientHeight, document.documentElement.clientHeight
)
let index = 0, timer

// 1초동안 이벤트 금지하기 (자연스러운 슬라이드 효과 연출)
function trotthling(handler, e){
  if(!timer){
    timer = setTimeout(function(){
      handler(e)
      timer = null 
    }, 1000)
  }
}

function changeSlide(e){
  console.log('scroll', e.deltaY)
  
  if(e.deltaY > 0){ // 스크롤 내린 경우
    index++
  }
  if(index < sections.length){ // 섹션 이동하기
    main.style.marginLeft = -1 * index * 100 + 'vw'
  }
  
}

document.addEventListener('wheel', (e) => trotthling(changeSlide, e))

 

* 연습과제 9

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="text">Hello World!</div>
  <main>
    <section><p>1</p></section>
    <section><p>2</p></section>
    <section><p>3</p></section>
    <section><p>4</p></section>
  </main>
  <script src="app.js"></script>
</body>
</html>
body{
  margin: 0; padding: 0;
  box-sizing: border-box;
  background-color: #0e1111;
}
main{
  width: 100vw; height: 100vh;
  display: flex;
  justify-content: center;
  overflow: hidden;
}
main section{
  width: 25%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: 1s ease-in-out;
}
main section p{
  font-size: 2rem;
  font-weight: bold;
  color: seashell;
  text-align: center;
  margin: 0; padding: 1rem 2rem;
  /* border: 1px solid red; */
}
main section:nth-child(1){
  background-color: yellowgreen;
}
main section:nth-child(2){
  background-color: brown;
}
main section:nth-child(3){
  background-color: yellow;
}
main section:nth-child(4){
  background-color: springgreen;
}
main section:nth-child(2n+1){
  transform: translateY(-100vh);
}
main section:nth-child(2n){
  transform: translateY(100vh);
}
.text{
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  color: yellowgreen; font-size: 5rem;
  /* border: 1px solid red; */
}
main section.show{
  transform: translateY(0);
}
.expand{
  width: 100%;
}
const text = document.querySelector('.text')
const main = document.querySelector('main')
const sections = main.querySelectorAll('section')


function showSlides(e){
  if(e.deltaY > 0){
    text.innerText = ''
    for(let section of sections){
      section.classList.add('show')
    }
  }
}
function expandSlide(e){
  console.log(e.target)
  if(e.target.className === 'show'){
    for(let section of sections){
      if(section !== e.target){
        section.style.width = '0'
        setTimeout(function(){
          section.innerHTML = ''
        }, 1000)
      }
    }
    e.target.classList.add('expand')
  }
}

main.addEventListener('click', expandSlide)
window.addEventListener('wheel', showSlides)
728x90