[TIL] 211002

Lee SyongΒ·2021λ…„ 10μ›” 2일
0

TIL

λͺ©λ‘ 보기
45/204
post-thumbnail

πŸ“ 였늘 ν•œ 것

  1. λ―Έλ‹ˆ κ²Œμž„ κ΅¬ν˜„ 쀑 (1μ°¨ μˆ˜μ • μ™„λ£Œ)

πŸ“š 배운 것

λ‹Ήκ·Όλ§Œ 클릭해야 ν•˜λŠ” κ²Œμž„(?)


⭐ κ°•μ˜ μ°Έκ³ ν•΄ μ½”λ“œ μˆ˜μ •

처음 μ™„μ„± μ½”λ“œ
μ–΄μ œ ν’€μ΄μ—μ„œ μ΄μ–΄μ„œ

3. κ²Œμž„ μ‹œμž‘ν•˜κΈ°

κ²Œμž„ μ‹œμž‘ λ²„νŠΌμ„ ν΄λ¦­ν•˜λ©΄

1) field λ‚΄λΆ€κ°€ λ¦¬μ…‹λœ ν›„, ν•„λ“œ μ•ˆμ— λ‹Ήκ·Ό 10개과 벌레 7λ§ˆλ¦¬κ°€ λžœλ€ν•œ μœ„μΉ˜μ— λ°°μΉ˜λ˜μ–΄μ•Ό ν•œλ‹€.
2) 타이머가 μž‘λ™ν•΄μ•Ό ν•œλ‹€. (10μ΄ˆλΆ€ν„° μ‹œμž‘)
3) 클릭 κ°€λŠ₯ νšŸμˆ˜κ°€ 10으둜 λ°”λ€Œμ–΄μ•Ό ν•œλ‹€.
4) κ²Œμž„ μ‹œμž‘ λ²„νŠΌ μ•ˆμ˜ μ•„μ΄μ½˜μ΄ 쀑지 μ•„μ΄μ½˜μœΌλ‘œ λ°”λ€Œμ–΄μ•Ό ν•œλ‹€.

πŸ“Œ λ‚΄κ°€ 처음 μž‘μ„±ν•œ μ½”λ“œ

λ”°λ‘œ μ—†μ—ˆλ‹€. μ•„λž˜μ™€ 같이 κ·Έλƒ₯ λ°”λ‘œ startBtn 이벀트 λ¦¬μŠ€λ„ˆ μ•ˆμ— if ꡬ문을 λ§Œλ“€μ–΄μ„œ μ­‰ 써쀬음.

startBtn.addEventListener('click', function startTimer () {
  if (document.querySelector('i[class$="stop"]') === null) { // κ²Œμž„ μ‹œμž‘ μ „, κ²Œμž„ μ‹œμž‘

    placeImgRandomly();

    timer.innerHTML = `0:10`;
    startWorkingTimer();

    clickLimit.innerHTML = 10;

    bg_mp3.play();

    startBtn.innerHTML = '<i class="fas fa-stop"></i>';

  } else { // κ²Œμž„ μ‹œμž‘ ν›„, κ²Œμž„ 정지

    /* μ½”λ“œ μ€‘λž΅ */
  
  }
});

πŸ’‘ κΈ°λ³Έ μˆ˜μ •

  • κ²Œμž„ μƒνƒœλ₯Ό κΈ°μ–΅ν•˜λŠ” μ „μ—­ λ³€μˆ˜λ₯Ό μΆ”κ°€ν–ˆλ‹€. (started, timer, limit) κ²Œμž„ 진행 μƒνƒœμ— 따라 κ·Έ 값을 λ°”κΏ”μ€˜μ•Ό ν•˜λŠ” λ³€μˆ˜μ΄λ―€λ‘œ let을 μ‚¬μš©ν•΄ global scope에 μ„ μ–Έν•΄μ•Ό ν•œλ‹€.

  • startBtn의 이벀트 λ¦¬μŠ€λ„ˆ 본문을 μˆ˜μ •ν–ˆλ‹€.

    • if ꡬ문의 쑰건식을, started λ³€μˆ˜λ₯Ό μ΄μš©ν•΄ λ°”κΏ¨λ‹€.
      if (document.querySelector('[class$="stop"]') β†’ if (started)

    • μ—¬κΈ°μ €κΈ° 써놓은 μ½”λ“œλ“€μ„ μˆ˜μ • 및 정리해 global scope에 startGame ν•¨μˆ˜μ™€ stopGame ν•¨μˆ˜λ₯Ό λ§Œλ“€μ—ˆλ‹€.

  • fieldλ₯Ό λ¦¬μ…‹ν•˜κΈ° μœ„ν•΄ μ μ—ˆλ˜ μ½”λ“œλ₯Ό κ°„κ²°ν•˜κ²Œ λ°”κΏ¨λ‹€. μ•„λž˜ μ½”λ“œλ₯Ό field.innerHTML = '' 둜 λ°”κΏ”μ„œ 'λ‹Ήκ·Ό, 벌레 이미지 랜덀 배치 ν•¨μˆ˜ μ•ˆ'에 μ‚½μž…ν•¨. 졜고둜 λΉ„νš¨μœ¨μ μ΄μ—ˆ

// λ‹Ήκ·Ό, 벌레 제거 & replay μ°½ 제거
function init ()  {
  const carrots = document.querySelectorAll('img[alt="carrot"]');
  const bugs = document.querySelectorAll('img[alt="bug"]');

  for (let i = 0; i < carrots.length; i++) {
    carrots[i].remove();
  }

  for (let i = 0; i < bugs.length; i++) {
    bugs[i].remove();
  }

  const result = document.querySelector('.result');
  result.remove();
}

πŸ’‘ λ‹Ήκ·Ό, 벌레 랜덀 배치 ν•¨μˆ˜

[TIL] 211001 μ°Έκ³ 


πŸ’‘ 타이머 μ‹œμž‘ ν•¨μˆ˜

πŸ”Ž λ‚΄κ°€ 처음 μž‘μ„±ν•œ μ½”λ“œ

  • 타이머λ₯Ό 10μ΄ˆλΆ€ν„° μ‹œμž‘ν•˜λ©΄ κ²Œμž„ μ‹œμž‘ν•˜κ³  1μ΄ˆκ°€ μ§€λ‚˜μ•Ό 10이 뜸. κ·Έλž˜μ„œ λ‚΄κ°€ μž‘μ„±ν•  λ•ŒλŠ” 10을 μ΄ˆκΈ°κ°’μœΌλ‘œ λ”°λ‘œ ν‘œμ‹œν•΄μ€€ ν›„ νƒ€μ΄λ¨ΈλŠ” 9λΆ€ν„° μ‹œμž‘ν•˜λŠ” μΌμ’…μ˜ 꼼수λ₯Ό μ‚¬μš©ν–ˆλ‹€.
// 각 이벀트 λ¦¬μŠ€λ„ˆ ν•¨μˆ˜μ— μ μ–΄μ€Œ
timer.innerHTML = `0:10`;
// 타이머 κ΄€λ ¨
let time = 9;
let decreNum;

function startWorkingTimer () {
  decreNum = setInterval(function () {
    const second = time % 60;
    timer.innerHTML = `0:${second}`;
    time--;

    if (time < 0) {
      clearInterval(decreNum);

      bg_mp3.pause();

      startBtn.style.visibility = 'hidden';

      displayResult('YOU LOST πŸ’©');
    }
  }, 1000);
}

πŸ”Ž κ°•μ˜λ₯Ό μ°Έκ³ ν•΄ 처음 μˆ˜μ •ν•œ μ½”λ“œ

  • κ²Œμž„μ„ μ‹œμž‘ν•œ ν›„ 1μ΄ˆκ°€ μ§€λ‚˜μ•Όλ§Œ 10이 λœ¨λŠ” 문제 λ°œμƒ
// 타이머 μ‹œμž‘ ν•¨μˆ˜
function startTimer() {
  let remainingSec = 10;
  const decNum = setInterval(function () {
    let minute = Math.floor(remainingSec / 60);
    let second = remainingSec % 60;
    timer.innerHTML = `${minute}:${second}`;
    remainingSec--;
  }, 1000);
}

πŸ”Ž μˆ˜μ • 및 μ™„μ„±

  • displayTime ν•¨μˆ˜λ₯Ό setInterval λ©”μ„œλ“œλ³΄λ‹€ λ¨Όμ € μ¨μ€˜μ•Ό, κ²Œμž„ μ‹œμž‘ ν›„ 1초λ₯Ό 기닀리지 μ•Šκ³ λ„ 10μ΄ˆκ°€ 화면에 ν‘œκΈ°λœλ‹€.

  • κ·Έ ν›„ setInterval λ©”μ„œλ“œ μ•ˆμ˜ diplayTime(--reaminingTime) 을 톡해 1μ΄ˆμ”© 쀄어든 μ‹œκ°„μ„ ν‘œκΈ°ν•  수 μžˆλ‹€.

// κ²Œμž„ μƒνƒœλ₯Ό κΈ°μ–΅ν•˜λŠ” μ „μ—­ λ³€μˆ˜λ“€
let timer = undefined;

// 숫자 κ΄€λ ¨ μ „μ—­ λ³€μˆ˜λ“€
const GAME_DURATION_TIME = 10; // 초 λ‹¨μœ„

// 타이머 μ‹œμž‘ ν•¨μˆ˜
function startTimer() {
  let remainingSec = GAME_DURATION_TIME;
  displayTime(remainingSec);
  timer = setInterval(() => {
    if (remainingSec <= 0) {
      clearInterval(timer);
      return;
    }
    displayTime(--remainingSec);
  }, 1000);
}

// 남은 μ‹œκ°„ ν‘œκΈ° ν•¨μˆ˜
function displayTime (time) {
  let minutes = Math.floor(time / 60);
  let seconds = time % 60;
  gameTimer.innerHTML = `${minutes}:${seconds}`;
}

πŸ’‘ 클릭 μ œν•œ 횟수 10으둜 μ„€μ •

πŸ”Ž λ‚΄κ°€ 처음 μž‘μ„±ν•œ μ½”λ“œ

startBtn.addEventListener('click', function () {
  clickLimit.innerHTML = 10;
});

πŸ”Ž κ°•μ˜λ₯Ό μ°Έκ³ ν•΄ μˆ˜μ • 및 μ™„μ„±

  • 'λ‹Ήκ·Ό, 벌레 이미지 랜덀 배치 ν•¨μˆ˜'인 placeRandomly ν•¨μˆ˜μ˜ 이름을 initGame으둜 이름을 λ°”κΎΈκ³ , 클릭 μ œν•œ 횟수λ₯Ό 10 즉, λ‹Ήκ·Όμ˜ 개수둜 μ„€μ •ν•˜λŠ” μ½”λ“œκΉŒμ§€ ν•¨κ»˜ λ„£μ–΄μ£Όμ—ˆλ‹€.
// κ²Œμž„ μ΄ˆκΈ°ν™” (λ‹Ήκ·Όκ³Ό 벌레 이미지 랜덀 배치 / 클릭 μ œν•œ 횟수 10으둜 μ„€μ •)
function initGame () {
  field.innerHTML = '';

  makeImg('carrot', CARROT_COUNT, 'img/carrot.png', CARROT_SIZE);
  makeImg('bug', BUG_COUNT, 'img/bug.png', BUG_SIZE);

  gameLimit.innerHTML = CARROT_COUNT;
  
  bg_mp3.play();
}

πŸ’‘ κ²Œμž„ μ‹œμž‘ λ²„νŠΌ β†’ 쀑지 λ²„νŠΌ

πŸ”Ž λ‚΄κ°€ 처음 μž‘μ„±ν•œ μ½”λ“œ

startBtn.addEventListener('click', function () {
  startBtn.innerHTML = '<i class="fas fa-stop"></i>';
});

πŸ”Ž κ°•μ˜λ₯Ό μ°Έκ³ ν•΄ μˆ˜μ • 및 μ™„μ„±

// κ²Œμž„ 쀑지 μ•„μ΄μ½˜ ν‘œκΈ° ν•¨μˆ˜
function showStopIcon () {
  const icon = document.querySelector('.fa-play');
  icon.classList.add('fa-stop');
  icon.classList.remove('fa-play');
}

πŸ“Œ 'κ²Œμž„ μ‹œμž‘ν•˜κΈ°' μ΅œμ’… μˆ˜μ • μ½”λ“œ

'use strict';

// htmlμ—μ„œ λ°›μ•„μ˜¨ μš”μ†Œλ“€
const startBtn = document.querySelector('.setting-startBtn');
const gameTimer = document.querySelector('.setting-timer');
const gameLimit = document.querySelector('.setting-limit');
const field = document.querySelector('.field');

// κ²Œμž„ μƒνƒœλ₯Ό κΈ°μ–΅ν•˜λŠ” μ „μ—­ λ³€μˆ˜λ“€
let started = false;
let timer = undefined;
let limit = 0;

// 숫자 κ΄€λ ¨ μ „μ—­ λ³€μˆ˜λ“€
const CARROT_COUNT = 10;
const BUG_COUNT = 7;
const CARROT_SIZE = 120;
const BUG_SIZE = 80;
const GAME_DURATION_TIME = 10;


// κ²Œμž„ μ‹œμž‘ λ²„νŠΌ 클릭 μ‹œ
startBtn.addEventListener('click', () => {
  if (started) {
    stopGame();
  } else {
    startGame();
  }
  started = !started;
});

// κ²Œμž„ μ‹œμž‘ ν•¨μˆ˜
function startGame () {
  // κ²Œμž„ μ΄ˆκΈ°ν™”
  initGame();

  // 타이머 μ‹œμž‘
  startTimer();

  // κ²Œμž„ μ‹œμž‘ μ•„μ΄μ½˜ β†’ κ²Œμž„ 쀑지 μ•„μ΄μ½˜
  showStopIcon();
}


// κ²Œμž„ μ΄ˆκΈ°ν™” (λ‹Ήκ·Όκ³Ό 벌레 이미지 랜덀 배치 / 클릭 μ œν•œ 횟수 10으둜 μ„€μ • )
function initGame () {
  makeImg('carrot', CARROT_COUNT, 'img/carrot.png', CARROT_SIZE);
  makeImg('bug', BUG_COUNT, 'img/bug.png', BUG_SIZE);

  gameLimit.innerHTML = CARROT_COUNT;
}

// 이미지 생성 ν•¨μˆ˜
function makeImg (name, count, src, size) {
  for (let i = 0; i < count; i++) {
    const item = document.createElement('img');
    item.setAttribute('class', `${name}`);
    item.setAttribute('src', `${src}`);

    const coord = randomCoord(size);
    item.style.left = coord[0] + 'px';
    item.style.top = coord[1] + 'px';
    item.style.width = size + 'px';

    field.appendChild(item);
  }
}

// 랜덀 μ’Œν‘œ 생성 ν•¨μˆ˜
function randomCoord (size) {
  const fieldRect = field.getBoundingClientRect();

  return [ ( fieldRect.width - size ) * Math.random(),
           ( fieldRect.height - size ) * Math.random() ];
}

// 타이머 μ‹œμž‘ ν•¨μˆ˜
function startTimer() {
  let remainingSec = GAME_DURATION_TIME;
  displayTime(remainingSec);
  timer = setInterval(() => {
    if (remainingSec <= 0) {
      clearInterval(timer);
      return;
    }
    displayTime(--remainingSec);
  }, 1000);
}

// 남은 μ‹œκ°„ ν‘œκΈ° ν•¨μˆ˜
function displayTime (time) {
  let minutes = Math.floor(time / 60);
  let seconds = time % 60;
  gameTimer.innerHTML = `${minutes}:${seconds}`;
}

// κ²Œμž„ 쀑지 μ•„μ΄μ½˜ ν‘œκΈ° ν•¨μˆ˜
function showStopIcon () {
  const icon = document.querySelector('.fa-play');
  icon.classList.add('fa-stop');
  icon.classList.remove('fa-play');
}

4. κ²Œμž„ μ •μ§€ν•˜κΈ°

κ²Œμž„ 정지 λ²„νŠΌμ„ ν΄λ¦­ν•˜λ©΄

1) 타이머가 λ©ˆμΆ°μ•Ό ν•œλ‹€.
2) κ²Œμž„ μ‹œμž‘ λ²„νŠΌμ΄ μ—†μ–΄μ Έμ•Ό ν•œλ‹€.
3) REPLAY 문ꡬ와 ν•¨κ»˜ μž¬μ‹œμž‘ λ²„νŠΌμ΄ λ– μ•Ό ν•œλ‹€.

πŸ“Œ λ‚΄κ°€ 처음 μž‘μ„±ν•œ μ½”λ“œ

startBtn.addEventListener('click', function startTimer () {
  if (document.querySelector('i[class$="stop"]') === null) { // κ²Œμž„ μ‹œμž‘ μ „, κ²Œμž„ μ‹œμž‘
  
    /* μ½”λ“œ μ€‘λž΅ */
 
  } else { // κ²Œμž„ μ‹œμž‘ ν›„, κ²Œμž„ 정지
    bg_mp3.pause();

    const alert_wav = new Audio('sound/alert.wav');
    alert_wav.play();

    clearInterval(decreNum);

    startBtn.style.visibility = 'hidden';

    displayResult('REPLAY ❓');
  }
});

πŸ“Œ 'κ²Œμž„ μ •μ§€ν•˜κΈ°' μ΅œμ’… μˆ˜μ • μ½”λ“œ

βœ” HTML

  • div.result μœ„μΉ˜λ₯Ό field μ•ˆμ—μ„œ λ°–μœΌλ‘œ μˆ˜μ •ν–ˆλ‹€.
<html>
  <body>
    <div class="setting">
      <div class="setting-box">
        <button class="setting-startBtn" type="button"><i class="fas fa-play"></i></button>
        <div class="setting-timer">00:00</div>
        <div class="setting-limit">0</div>
      </div>
    </div>
    <div class="field"></div>
    <div class="result">
      <button class="replayBtn" type="button"><i class="fas fa-redo"></i></button>
      <p class="notice"></p>
    </div>
  </body>
</html>

βœ” CSS

.field {
  height: calc(100vh - 450px);
  position: relative;
}

.result {
  width: 50%;
  margin: 0 auto;
  padding: 10px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  border-radius: 20px;
  background-color: rgba(0, 0, 0, 0.5);
  text-align: center;
  font-size: 3em;
  color: white;
  z-index: 1;
  display: none;
}

βœ” JavaScript

const field = document.querySelector('.field');
const gameResult = document.querySelector('.result');
const replayBtn = document.querySelector('.replayBtn');
const notice = document.querySelector('.notice');

// κ²Œμž„ μ‹œμž‘ λ²„νŠΌ 클릭 μ‹œ
startBtn.addEventListener('click', () => {
  if (started) {
    stopGame();
  } else {
    startGame();
  }
  started = !started;
});

// κ²Œμž„ 쀑지 ν•¨μˆ˜ (started 값이 true인 μƒνƒœμ—μ„œ startBtn을 λ‹€μ‹œ ν΄λ¦­ν•˜λ©΄)
function stopGame () {
  // κ²Œμž„ μ‹œμž‘ λ²„νŠΌ μˆ¨κΉ€
  hideStartBtn();

  // 타이머 정지
  stopTimer();

  // REPLAY 문ꡬ, μž¬μ‹œμž‘ λ²„νŠΌ ν‘œκΈ°
  showGameResult('Replay ❓');
}

// 타이머 정지 ν•¨μˆ˜
function stopTimer () {
  clearInterval(timer);
}

// κ²Œμž„ μ‹œμž‘ λ²„νŠΌ μˆ¨κΉ€ ν•¨μˆ˜
function hideStartBtn () {
  startBtn.style.visibility = 'hidden';
}

// κ²°κ³Ό 문ꡬ, μž¬μ‹œμž‘ λ²„νŠΌ ν‘œκΈ° ν•¨μˆ˜
function showGameResult (text) {
  gameResult.style.display = 'block';
  notice.innerHTML = text;
}

5. μž¬μ‹œμž‘ λ²„νŠΌμ„ ν΄λ¦­ν•˜λ©΄

μž¬μ‹œμž‘ λ²„νŠΌμ„ ν΄λ¦­ν•˜λ©΄

1) 이전 κ²Œμž„μ˜ λ‹Ήκ·Όκ³Ό λ²Œλ ˆκ°€ μ—†μ–΄μ Έμ•Ό ν•œλ‹€.
2) replay ❓ 창이 μ—†μ–΄μ Έμ•Ό ν•œλ‹€.
3) λ‹€μ‹œ 2.κ°€ μΌμ–΄λ‚˜μ•Ό ν•œλ‹€. - (1), (3)만
4) 타이머가 'λ‹€μ‹œ μ²˜μŒλΆ€ν„°' μž‘λ™ν•΄μ•Ό ν•œλ‹€.
5) κ²Œμž„ μ‹œμž‘ λ²„νŠΌμ΄ λ‹€μ‹œ 보여야 ν•œλ‹€.

πŸ“Œ λ‚΄κ°€ 처음 μž‘μ„±ν•œ μ½”λ“œ

// field μ•ˆμ„ ν΄λ¦­ν–ˆμ„ λ•Œ
field.addEventListener('click', function (event) {
  // 8. 클릭된 게 μž¬μ‹œμž‘ λ²„νŠΌμ΄λ©΄
  if (event.target.className === 'fas fa-redo') {

    init();

    placeImgRandomly();

    clickLimit.innerHTML = 10;

    timer.innerHTML = `0:10`;
    time = 9;
    startWorkingTimer();

    bg_mp3.currentTime = 0;
    bg_mp3.play();

    startBtn.style.visibility = 'visible';
  }
});

πŸ’‘ κ°•μ˜λ₯Ό 보기 μ „ μˆ˜μ •ν•œ μ½”λ“œ

// μž¬μ‹œμž‘ λ²„νŠΌ 클릭
replayBtn.addEventListener('click', () => {
  started = true;
  // 이전 λ‹Ήκ·Όκ³Ό 벌레 이미지 μ—†μ• κ³ , μƒˆλ‘­κ²Œ λ‹Ήκ·Ό 벌레 이미지 랜덀 배치 & 클릭 μ œν•œ 횟수 10으둜 μ„€μ •
  initGame();

  // 타이머 λ‹€μ‹œ μ²˜μŒλΆ€ν„° μž‘λ™
  startTimer ();

  // κ²°κ³Ό 문ꡬ, μž¬μ‹œμž‘ λ²„νŠΌ μˆ¨κΉ€
  hideGameResult();
});

πŸ“Œ κ°•μ˜λ₯Ό μ°Έκ³ ν•΄ μˆ˜μ •ν•œ μ½”λ“œ

  • initGame ν•¨μˆ˜μ™€ startTimer ν•¨μˆ˜ λŒ€μ‹ μ— startGame ν•¨μˆ˜λ₯Ό 넣어쀬닀. μ΄λ ‡κ²Œ ν•˜λ‹ˆκΉŒ startGame ν•¨μˆ˜ μ•ˆμ— μžˆλŠ” showStopBtn ν•¨μˆ˜μ—μ„œ 였λ₯˜κ°€ λ°œμƒν–ˆλ‹€. 처음 startBtn을 λˆŒλŸ¬μ„œ 없애버린 fa-playλΌλŠ” classλ₯Ό, λ‹€μ‹œ replayBtn을 λˆŒλŸ¬μ„œ 찾으렀고 ν•˜λ‹ˆ 였λ₯˜κ°€ λ°œμƒν•œ 것이닀. 였λ₯˜ 해결을 μœ„ν•΄ showStopBtn ν•¨μˆ˜μ—μ„œ fa-play λŒ€μ‹ μ— fasλ₯Ό μ μ–΄μ£Όμ—ˆλ‹€.
// μž¬μ‹œμž‘ λ²„νŠΌ 클릭
replayBtn.addEventListener('click', () => {
  started = true;
  // κ²Œμž„ μ‹œμž‘
  startGame();

  // κ²°κ³Ό 문ꡬ, μž¬μ‹œμž‘ λ²„νŠΌ μˆ¨κΉ€
  hideGameResult();
});

6. λ‹Ήκ·Όκ³Ό 벌레λ₯Ό ν΄λ¦­ν•˜λ©΄

πŸ“Œ λ‚΄κ°€ 처음 μž‘μ„±ν•œ μ½”λ“œ

// field μ•ˆμ„ ν΄λ¦­ν–ˆμ„ λ•Œ
field.addEventListener('click', function (event) {
  // 3. 클릭된 게 당근이면
  if (event.target.alt === 'carrot') {

    const carrot_mp3 = new Audio('sound/carrot_pull.mp3');
    carrot_mp3.play();

    clickLimit.innerHTML = clickLimit.innerHTML - 1;

    event.target.remove();

    // 5. μ œν•œ μ‹œκ°„ 내에 λͺ¨λ“  당근을 ν΄λ¦­ν•˜λ©΄
    if (time > 0) {
      if (document.querySelectorAll('img[alt="carrot"]').length === 0) {

        bg_mp3.pause();

        win_mp3.play();

        clearInterval(decreNum);

        startBtn.style.visibility = 'hidden';

        displayResult('YOU WON πŸŽ‰');
      }
    }
  }
});

// field μ•ˆμ„ ν΄λ¦­ν–ˆμ„ λ•Œ
field.addEventListener('click', function (event) {
  // 4. 클릭된 게 벌레라면
  if (event.target.alt === 'bug') {

    bg_mp3.pause();

    const bug_mp3 = new Audio('sound/bug_pull.mp3');
    bug_mp3.play();

    clearInterval(decreNum);

    startBtn.style.visibility = 'hidden';

    displayResult('YOU LOST πŸ’©');
  }
});

πŸ’‘ κ°•μ˜λ₯Ό 보기 μ „ μˆ˜μ •ν•œ μ½”λ“œ

  • κ°•μ˜μ—μ„œλŠ” μ‹œμž‘λΆ€ν„° scoreλ₯Ό μ‚¬μš©ν–ˆλŠ”λ° λ‚˜λŠ” 처음 μ μ—ˆλ˜ λŒ€λ‘œ 계속 limitλ₯Ό μ‚¬μš©ν–ˆλ”λ‹ˆ μž‘λ™μ΄ μ œλŒ€λ‘œ μ•ˆ λ˜λŠ” 뢀뢄이 λ‚˜μ™”λ‹€.
// ν•„λ“œ 클릭
field.addEventListener('click', event => {
  if (!started) {
    return;
  } else {
    if (event.target.className === 'carrot') {
      clickCarrot(event);
    } else if (event.target.className === 'bug') {
      clickBug();
    }
  }
});

// λ‹Ήκ·Ό 클릭
function clickCarrot (event) {
  // λ‹Ήκ·Ό 제거
  event.target.remove();

  // 클릭 μ œν•œ 횟수 1 κ°μ†Œ
  let clickLimit = CARROT_COUNT;
  clickLimit = clickLimit - 1;
  gameLimit.innerHTML = clickLimit;
}

// 벌레 클릭
function clickBug () {
  started = false;

  // κ²Œμž„ μ‹œμž‘ λ²„νŠΌ μˆ¨κΉ€
  hideStartBtn();

  // 타이머 정지
  stopTimer();

  // YOU LOST 문ꡬ, μž¬μ‹œμž‘ λ²„νŠΌ ν‘œκΈ°
  showGameResult('YOU LOST');
}

πŸ“Œ κ°•μ˜λ₯Ό μ°Έκ³ ν•΄ μˆ˜μ •ν•œ μ½”λ“œ

  • 계속 limitλ₯Ό μ‚¬μš©ν•΄μ„œ μž‘μ„±ν•΄ λ΄„. μ˜λ„ν•œ λŒ€λ‘œ μ‹€ν–‰λ˜λŠ” κ±Έ ν™•μΈν–ˆλ‹€.

  • κ²Œμž„μ˜ μŠΉνŒ¨μ— 따라 화면을 λ°”κΎΈκΈ° μœ„ν•΄ finishGame ν•¨μˆ˜λ₯Ό μΆ”κ°€ν–ˆλ‹€. 쑰건뢀 μ‚Όν•­ μ—°μ‚°μžλ₯Ό μ΄μš©ν–ˆλ‹€. 타이머 ν•¨μˆ˜μ—λ„ finishGame ν•¨μˆ˜λ₯Ό μΆ”κ°€ν–ˆλ‹€.

// 숫자 κ΄€λ ¨ μ „μ—­ λ³€μˆ˜λ“€
const CARROT_COUNT = 10;
const GAME_DURATION_TIME = 10;

// κ²Œμž„ μƒνƒœλ₯Ό κΈ°μ–΅ν•˜λŠ” μ „μ—­ λ³€μˆ˜λ“€
let started = false;
let timer = undefined;
let limit = CARROT_COUNT;

// 타이머 μ‹œμž‘ ν•¨μˆ˜
function startTimer () {
  let remainingSec = GAME_DURATION_TIME;
  displayTime(remainingSec);
  timer = setInterval(() => {
    if (remainingSec <= 0) {
      clearInterval(timer);
      finishGame(limit === 0); // κ²Œμž„ μ’…λ£Œ
      return;
    }
    displayTime(--remainingSec);
  }, 1000);
}

// field 클릭
field.addEventListener('click', onFieldClick);

function onFieldClick (event) {
  if (!started) { // start 값이 false이면 클릭이 먹지 μ•ŠμŒ
    return;
  }
  const target = event.target;
  if (target.matches('.carrot')) {
    // λ‹Ήκ·Ό 제거
    target.remove();

    // 클릭 μ œν•œ 횟수 κ°μ†Œ
    showGameLimit(--limit);

    // κ²Œμž„ μ’…λ£Œ (WON)
    if (limit === 0) {
      finishGame(true);
    }
  } else if (target.matches('.bug')) {
    // κ²Œμž„ μ’…λ£Œ (LOST)
    finishGame(false);
  }
}

// 클릭 μ œν•œ 횟수 ν‘œκΈ° ν•¨μˆ˜
function showGameLimit (num) {
  gameLimit.innerHTML = num;
}

// κ²Œμž„ μ’…λ£Œ ν•¨μˆ˜ (WON λ˜λŠ” LOST)
function finishGame (win) {
  // κ²Œμž„ μ‹œμž‘ λ²„νŠΌ μˆ¨κΉ€
  hideStartBtn();

  // 타이머 정지
  stopTimer();

  // ~ 문ꡬ, μž¬μ‹œμž‘ λ²„νŠΌ ν‘œκΈ°
  showGameResult(win? 'YOU WON πŸŽ‰' :'YOU LOST πŸ’©');
}

7. μŒμ•… μž¬μƒ

πŸ“Œ λ‚΄κ°€ μ²˜μŒμ— μž‘μ„±ν•œ μ½”λ“œ

  • play()와 currentTime = 0 그리고 pause() λ₯Ό ν•˜λ‚˜ν•˜λ‚˜ λ‹€ 써쀬닀.

πŸ“Œ κ°•μ˜λ₯Ό μ°Έκ³ ν•΄ μˆ˜μ • 및 μ™„μ„±

  • μ΄λ ‡κ²Œ λ¬ΆλŠ” 방법은 μƒκ°ν–ˆλŠ”λ° ν˜Ήμ‹œλ‚˜ μŒμ•… μž¬μƒκ³Ό 정지λ₯Ό ν•œκΊΌλ²ˆμ— λ¬ΆλŠ” 방법이 μžˆμ„κΉŒ ν•΄μ„œ μˆ˜μ •ν•˜μ§€ μ•Šκ³  μžˆμ—ˆλ‹€. 그런데 μ—†λ‚˜ 보닀. 많이 짧은 λ‚΄μš©κΉŒμ§€ ν•¨μˆ˜λ‘œ 써야 ν•˜λ‚˜ κΆκΈˆν–ˆλŠ”λ° μ“΄λ‹€λŠ” κ±Έ μ•Œμ•˜λ‹€.
// μŒμ•… μž¬μƒ
function playSound (sound) {
  sound.currentTime = 0;
  sound.play();
}

// μŒμ•… 정지
function stopSound (sound) {
  sound.pause();
}

8. κ°•μ˜λ₯Ό μ°Έκ³ ν•΄ μˆ˜μ •ν•œ μ΅œμ’… μ½”λ“œ

πŸ”Ž HTML

<html>
  <body>
    <div class="setting">
      <div class="setting-box">
        <button class="setting-startBtn" type="button"><i class="fas fa-play"></i></button>
        <div class="setting-timer">00:00</div>
        <div class="setting-limit">0</div>
      </div>
    </div>
    <div class="field"></div>
    <div class="result">
      <button class="replayBtn" type="button"><i class="fas fa-redo"></i></button>
      <p class="notice"></p>
    </div>
  </body>
</html>

πŸ”Ž CSS

  • μΆ”κ°€μ μœΌλ‘œ 타이머와 클릭 μ œν•œ 횟수 μœ„μ— μ»€μ„œ μ˜¬λ Έμ„ λ•Œ ν…μŠ€νŠΈκ°€ μ„ νƒλ˜μ§€ μ•Šλ„λ‘ CSS μˆ˜μ •ν–ˆλ‹€. user-select: none 을 μ‚¬μš©ν•¨.

  • div.settingκ³Ό div.field의 heightλ₯Ό 450px κ³ μ • κ°’μ—μ„œ % κ°’μœΌλ‘œ μˆ˜μ •ν–ˆλ‹€. htmlκ³Ό body에도 height: 100% λ₯Ό 좔가함.

.setting {
  height: 50%;
  position: relative;
}

.field {
  height: calc(100vh - 50%);
  position: relative;
}
  • transition μ†μ„±μ˜ μœ„μΉ˜λ₯Ό μˆ˜μ •ν–ˆλ‹€. μš”μ†Œ:hover κ°€ μ•„λ‹ˆλΌ μš”μ†Œ μžμ²΄μ— κ±Έμ–΄μ•Ό λ§ˆμš°μŠ€κ°€ 올라갈 λ•Œμ™€ λ‚΄λ €κ°ˆ λ•Œ λͺ¨λ‘ μ• λ‹ˆλ©”μ΄μ…˜μ΄ μ μš©λΌμ„œ 더 μžμ—°μŠ€λŸ½λ‹€.
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

button {
  border: 5px solid black;
  border-radius: 20px;
  background-color: wheat;
  width: 100px;
  height: 100px;
  cursor: pointer;
  display: block;
  margin: 0 auto;
  transition: all 200ms ease;
}

button:hover {
  transform: scale(1.1);
}

img {
  position: absolute;
  transition: all 200ms ease;
}

img:hover {
  transform: scale(1.2);
}

html {
  height: 100%;
}

body {
  height: 100%;
  background: url("img/background.png") no-repeat center / cover;
  font-family: "Gowun Batang", serif;
}

.setting {
  height: 50%;
  position: relative;
}

.setting-box {
  position: absolute;
  top: 30px;
  left: 50%;
  transform: translateX(-50%);
}

.setting-timer {
  border: 5px solid black;
  border-radius: 20px;
  background-color: white;
  width: 260px;
  height: 80px;
  margin: 15px auto;
  text-align: center;
  font-size: 2.5em;
  line-height: 1.3;
  user-select: none;
}

.fas {
  font-size: 40px;
}

.setting-limit {
  border: 4px solid black;
  border-radius: 50%;
  background-color: orange;
  color: white;
  width: 100px;
  height: 100px;
  margin: 0 auto;
  text-align: center;
  line-height: 1.5;
  font-size: 3em;
  font-weight: bold;
  user-select: none;
}

.field {
  height: calc(100vh - 50%);
  position: relative;
}

.result {
  width: 50%;
  margin: 0 auto;
  padding: 40px;
  position: absolute;
  left: 50%;
  top: 70%;
  transform: translate(-50%, -50%);
  border-radius: 20px;
  background-color: rgba(0, 0, 0, 0.5);
  text-align: center;
  font-size: 3em;
  color: white;
  z-index: 1;
  display: none;
}

πŸ”Ž JavaScript

'use strict';

const startBtn = document.querySelector('.setting-startBtn');
const gameTimer = document.querySelector('.setting-timer');
const gameLimit = document.querySelector('.setting-limit');

const field = document.querySelector('.field');
const gameResult = document.querySelector('.result');
const replayBtn = document.querySelector('.replayBtn');
const notice = document.querySelector('.notice');

const CARROT_COUNT = 10;
const BUG_COUNT = 7;
const CARROT_SIZE = 120;
const BUG_SIZE = 80;
const GAME_DURATION_TIME = 10;

let started = false;
let timer = undefined;
let limit = CARROT_COUNT;

const bgSound = new Audio('sound/bg.mp3');
const alertSound = new Audio('sound/alert.wav');
const carrotSound = new Audio('sound/carrot_pull.mp3');
const bugSound = new Audio('sound/bug_pull.mp3');
const winSound = new Audio('sound/game_win.mp3');



startBtn.addEventListener('click', () => {
  if (started) {
    stopGame();
  } else {
    startGame();
  }
});

replayBtn.addEventListener('click', () => {
  started = true;
  startGame();
  hideGameResult();
  playSound(bgSound);
});

function stopGame () {
  started = false;
  hideStartBtn();
  stopTimer();
  showGameResult('Replay ❓');
  stopSound(bgSound);
  playSound(alertSound);
}

function startGame () {
  started = true;
  initGame();
  startTimer();
  showStopIcon();
  playSound(bgSound);
}



field.addEventListener('click', onFieldClick);

function onFieldClick (event) {
  if (!started) {
    return;
  }
  
  const target = event.target;
  
  if (target.matches('.carrot')) {
    target.remove();
    showGameLimit(--limit);
    playSound(carrotSound);
    
    if (limit === 0) {
      finishGame(true);
    }
  } else if (target.matches('.bug')) {
    finishGame(false);
  }
}

function showGameLimit (num) {
  gameLimit.innerHTML = num;
}

function finishGame (win) {
  hideStartBtn();
  stopTimer();
  
  showGameResult(win? 'YOU WON πŸŽ‰' :'YOU LOST πŸ’©');
  if (win) {
    stopSound(bgSound);
    playSound(winSound);
  } else {
    stopSound(bgSound);
    playSound(bugSound);
  }
}



function initGame () {
  limit = CARROT_COUNT;
  field.innerHTML = '';

  makeImg('carrot', CARROT_COUNT, 'img/carrot.png', CARROT_SIZE);
  makeImg('bug', BUG_COUNT, 'img/bug.png', BUG_SIZE);

  gameLimit.innerHTML = CARROT_COUNT;
}

function makeImg (name, count, src, size) {
  for (let i = 0; i < count; i++) {
    const item = document.createElement('img');
    item.setAttribute('class', `${name}`);
    item.setAttribute('src', `${src}`);

    const coord = randomCoord(size);
    item.style.left = coord[0] + 'px';
    item.style.top = coord[1] + 'px';
    item.style.width = size + 'px';

    field.appendChild(item);
  }
}

function randomCoord (size) {
  const fieldRect = field.getBoundingClientRect();

  return [ ( fieldRect.width - size ) * Math.random(),
           ( fieldRect.height - size ) * Math.random() ];
}



function startTimer () {
  let remainingSec = GAME_DURATION_TIME;
  displayTime(remainingSec);
  timer = setInterval(() => {
    if (remainingSec <= 0) {
      clearInterval(timer);
      finishGame(limit === 0);
      return;
    }
    displayTime(--remainingSec);
  }, 1000);
}

function stopTimer () {
  clearInterval(timer);
}

function displayTime (time) {
  let minutes = Math.floor(time / 60);
  let seconds = time % 60;
  gameTimer.innerHTML = `${minutes}:${seconds}`;
}



function showStopIcon () {
  const icon = document.querySelector('.fas');
  icon.classList.add('fa-stop');
  icon.classList.remove('fa-play');
  startBtn.style.visibility = 'visible';
}

function hideStartBtn () {
  startBtn.style.visibility = 'hidden';
}



function showGameResult (text) {
  gameResult.style.display = 'block';
  notice.innerHTML = text;
}

function hideGameResult () {
  gameResult.style.display = 'none';
}



function playSound (sound) {
  sound.currentTime = 0;
  sound.play();
}

function stopSound (sound) {
  sound.pause();
}

⭐ 배운 λ‚΄μš© κ°œκ΄„ 정리

  • κ²Œμž„ 진행 μƒνƒœλ₯Ό λ‚˜νƒ€λ‚΄λŠ” μ „μ—­ λ³€μˆ˜λ₯Ό ν™œμš©ν•  것.

  • μˆ«μžλŠ” λŒ€λ¬Έμžμ™€ 언더바λ₯Ό μ΄μš©ν•΄ λ³€μˆ˜λ‘œ 적어쀄 것.

  • μ•„λž˜ μ„Έ 쀄은 λͺ¨λ‘ 같은 μ½”λ“œμ΄λ‹€.

field.addEventListener('click', onFieldClick);

field.addEventListener('click', (event) => onFieldClick(event));

field.addEventListener('click', function (event) {
  onFieldClick(event);
})
  • setInterval λ©”μ„œλ“œ μ΄μš©ν•΄μ„œ 타이머 λ§Œλ“  ν›„ μ‹œκ°„ ν‘œκΈ°ν•˜κΈ°.

  • ν•œ μ€„μ§œλ¦¬ 짧은 μ½”λ“œλΌλ„ λ‚΄μš©μ— 맞게 ν•¨μˆ˜λ‘œ λ§Œλ“€μ–΄ 쓰도둝 ν•œλ‹€. ꡬꡬ절절 if ꡬ문을 λŠ˜μ–΄λ†“μ§€ 말고 ν•¨μˆ˜λ₯Ό ν™œμš©ν•  것.

  • if ꡬ문 μ‘°κ±΄μ‹μ—μ„œ λ³€μˆ˜ 자체의 ture/false κ°’ ν™œμš©ν•˜κΈ°. 쑰건뢀 μ‚Όν•­ μ—°μ‚°μž λ‹€μ‹œ ν•œ 번 정리.

  • ν•¨μˆ˜λ₯Ό μ„ μ–Έν•  λ•Œ κ·Έ ν•¨μˆ˜κ°€ μ‹€ν–‰λ˜μ–΄μ„  μ•ˆ λ˜λŠ” κ²½μš°κ°€ μžˆλ‹€λ©΄, μ²˜μŒμ— if ꡬ문과 return을 μ΄μš©ν•˜μ—¬ κ·Έ λ°‘μ˜ μ½”λ“œλ₯Ό 읽지 μ•Šκ³  λ°”λ‘œ κ·Έ ν•¨μˆ˜λ₯Ό νƒˆμΆœν•˜λ„λ‘ ν•˜λŠ” 게 μ’‹λ‹€.

  • κ°•μ˜μ—μ„œλŠ” κ΅¬ν˜„ν•΄μ•Ό ν•  λ‚΄μš©μ„ μͺΌκ°  ν›„, κ·Έ λ‚΄μš©μ„ λ‹΄κ³  μžˆλŠ” ν•¨μˆ˜μ˜ 이름을 λ¨Όμ € μ μ–΄μ„œ ν˜ΈμΆœν•΄μ£Όκ³ , κ·Έ 밑에 κ·Έ ν•¨μˆ˜λ₯Ό μ„ μ–Έν–ˆλ‹€.

  • κ·Έλ™μ•ˆ ν•¨μˆ˜λŠ” μ˜ˆμ „μ˜ for ꡬ문처럼 왠지 λͺ¨λ₯΄κ²Œ κΈ°ν”Όν•˜κ³  싢은 것 쀑 ν•˜λ‚˜μ˜€λŠ”λ° 이번 μ‹€μŠ΅μ„ 톡해 μ „λ³΄λ‹€λŠ” 쑰금 μ΅μˆ™ν•΄μ§„ κ±° κ°™λ‹€. 이번 μ‹€μŠ΅μ˜ κ°€μž₯ 큰 μˆ˜ν™• κ°™λ‹€. 이제 λ¦¬νŒ©ν† λ§ νŒŒνŠΈκ°€ λ‚¨μ•˜λ‹€.


✨ 내일 ν•  것

  1. κ°•μ˜ 계속 λ“£κΈ° (λ¦¬νŒ©ν† λ§)
profile
λŠ₯λ™μ μœΌλ‘œ μ‚΄μž, ν–‰λ³΅ν•˜κ²ŒπŸ˜

0개의 λŒ“κΈ€