[Javascript 30] 1. Drum-Kit

tino-kim·2022년 3월 6일
0
post-thumbnail

💗 [Javascript 30] 1. Drum-Kit

✔ 레이아웃 짜기.

일단 flex-box를 이용해서 9개의 박스를 한 줄로 가운데에 위치시킬 것이다. 뒷배경은 무료 템플릿을 이용할 예정이다.

✔ 일단 우리가 채크해야되는 부분
1. 키보드의 특정 키와 소리가 연결되어 있어야 한다.
2. 키를 누르는 순간 소리가 나고, 검은색 테두리가 노란색 테두리로 변하고, 키의 크기가 커졌다가 작아져야 한다. 그리고 박스 쉐도우도 설정해준다.
3. 계속 반복해서 눌러도 소리가 계속 나야한다.

이정도로 생각하고 코드를 짜주면 될 것 같다.

✔ HTML 코드 짜기.

일단 눈에 보이는 부분 먼저 짜주기.
flex-box를 이용하기 위해서 section을 이용하고, class를 지정해서 감싸주었다.

<!DOCTYPE html>
<html lang="ko">
  <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>[Javascript30] Drum-Kit</title>
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Black+Han+Sans&family=Hubballi&family=Nanum+Gothic+Coding&family=PT+Sans+Narrow:wght@700&family=Smooch+Sans&display=swap"
      rel="stylesheet"
    />
    <link rel="stylesheet" href="style drumKit.css" />
    <script src="app drumKit.js"></script>
  </head>
  <body>
    <section class="keys">
      <section class="key">A</section>
      <section class="key">S</section>
      <section class="key">D</section>
      <section class="key">F</section>
      <section class="key">G</section>
      <section class="key">H</section>
      <section class="key">J</section>
      <section class="key">K</section>
      <section class="key">L</section>
    </section>
  </body>
</html>

✔ Data-*

data-* 은 추가적인 정보를 저장하고, 복잡하고 강력한 프로그램 객체를 만드려고 이용한다.

💙 data-*에 접근하는 방법
1. dataset을 통해서 접근하기.
2. data 속성에 접근하기.

✔ 참고 블로그 : Custom Data Attributes
여러 개의class를 사용하지 않고, 원하는 값을 추출할 수 있어서 굉장히 좋다.

💙 여러 개의 class 필요? 그저 접근 방법에 따라서 접근하면 된다.

✔ kbd

키보드 입력, 음성 입력 등 임의의 장치를 이용한 사용자의 입력을 나타낸다.

✔ CSS style 설정해주기.

일단 눈에 보이는 부분 먼저 짜주기.
flex-box를 이용해서 위치를 원하는 곳에 놓아주었다.

display: flex;
flex-direction: row;
justify-content: center; // 수평 방향으로 가운데에 위치하기.
align-items: center; // 수직 방향으로 가운데에 위치하기.
height: 100vh; // 높이가 화면의 100%를 꽉 채우게 한다.

✔ 100vh 100% 라고 한다.
vh은 부모 태그에 전혀 영향을 받지 않고, 화면의 100%로 꽉채워주는데, 100%로 하면 잘 안되는 경우가 많다.. 원래 코드는 min-height라고 해줬는데 나는 그냥 height라고 해주었다.
min-height는 최소 높이를 설정해주는 것인데 height라고 지정해도 잘 돌아가기 때문이다. 불안하면 min-height라고 지정해도 될 것 같다.

@import "reset.css";

body {
  background: no-repeat url("edm.jpg");
  background-size: cover;
  font-family: "Black Han Sans", sans-serif;
  font-family: "Hubballi", cursive;
  font-family: "Nanum Gothic Coding", monospace;
  font-family: "PT Sans Narrow", sans-serif;
  font-family: "Smooch Sans", sans-serif;
  font-size: 80px;
}

.keys {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  height: 100vh; /* 부모 태그와 관계없이 Viewpoint Height의 100%를 사용하겠다는 의미이다. */
  color: white;
}

.key {
  padding: 30px 40px;
  border: 3px solid #5d636c;
  margin: 20px 20px;
  border-radius: 5px;
}

이런 느낌으로 만들어주었다.

✔ Box-shadow

✔ 공식 문서 : Box-shadow Document

  • 4개의 요소를 가진 경우에는 이 순서대로 들어간다.
  1. offset-x: 수직 방향으로 얼마만큼의 크기인가?
  2. offset-y: 수평 방향으로 얼마만큼의 크기인가?
  3. blur: 얼마만큼 그림자가 블러처리되었는가?
  4. color: 그림자의 색이 어떤가?

여기서 box-shadow와 text-shadow를 이용해서 좀 더 예쁘게 만들어주었다. (text-shadow와 box-shadow 아주 비슷하다.) 그리고 text-align 등을 이용해서 텍스트가 중간에 위치할 수 있도록 조정해주었다.

✔ Web Audio API 이용하기.

이제 API를 이용할 것인데, html과 css 파일을 좀 변경해줄 것이다. (원하는 방향으로~!)

이런 느낌.. context 제작하고 효과음 연결하고, 어디로 소리가 나올 것인지 결정하고 연결해야한다.
✔ 참고 문서 : MDN || Web API Document

✔ Vanilla Javascript 코드 짜기.

✔ 음원 무료 다운로드 사이트 : 무료 음원 다운로드 사이트 || FreeSound

  1. 일단 키를 눌렀을 때, 해당 키와 연결된 소리가 나올 수 있게 코드를 작성한다. -> 그런데 에러가 생겼다.

😡 CORB ERROR
CORB (Cross-Origin Read Blocking): 웹 페이지에 도달하기 전에 웹 브라우저에서 의심스러운 교차 출처 리소스 로드를 식별하고 차단할 수 있는 알고리즘이다. CORB는 교차 출처 웹 페이지에서 더 멀리 떨어져 민감한 데이터를 누출할 위험을 줄인다. 📢 그러나, 해결되지 않아서 (내 생각에는 잘 인식되지 않는 사이트는 컴퓨터가 본인을 보호하기 위해서 차단하고 있는 것 같았다.) 그냥 다운로드 받았다. 그리고 항상 저작권에 유의해야하기 때문에 "This sound is public domain." 라고 적힌 음원만 다운로드 받아서 사용하였다. 음원이 없는 경우에는 null이 뜬다. 없는 경우에는 "stop the function from runnong all together." 처리를 시켜주고, 있는 경우에는 소리를 나오게 만들어준다.

  1. 계속 연타로 눌렀는데 한번 소리가 난다. -> (문제점) 계속 누르는 만큼 소리가 나지 않는다.

인지하는데에 시간이 걸리기 때문이다.
그래서 되감기를 해줘야 한다.

  1. 애니메이션을 추가해야 한다. 눌렀을 때, 노란색 테두리가 생기면서 커져야한다.

😡 추가하기 위해서 add를 이용해서 추가했더니, 다른 에러가 나왔다.

✔ Error가 발생하는 2가지 이유
1. DOM 요소가 존재하지 않기 때문이다.
2. DOM 요소가 선언된 HTML 위에 JS script를 놓았기 때문이다.
✔ 참고 블로그 : Cannot read property 'classList' of Null in JS
✔ 체크리스트 이용 방법 : CheckList || Tistory
근데 다 문제가 없는데 자꾸 Error가 뜬다...

🤚 이 부분은 질문하기... 도저히 모르겠다...
(정답) 오타였다... 조심하자... ㅈㅂㅈㅂ...

  1. 애니메이션이 끝난 뒤에 다시 원상복귀 되어야 한다.

노란색 테두리가 사라지지 않는 이유!
아직 클래스를 제거하지 않았기 때문이다.. 말 그대로 추가만 했을 뿐이다.
✔ 타이머 설정하는 함수를 만드는 것 <<< 사물이 스스로 애니메이션을 멈출 때 발생하는 전환된 종료 이벤트를 사용하는 것...! transition 시간이 정해져 있기 때문에, 가능하다. (성능 상 더 좋음.)
✔ 모든 당일 요소를 가져다가 반복하고 -> 이벤트 리스너에 연결시키기.
참고 문서 : forEach || MDN Document

✔ classList

✔ return

✔ boolean

✔ 추가적인 내용...

  • 오디오를 컨트롤 하는 방법 : Controlling audio
    currentTime = 0은 되감기를 의미한다.
    currentTime = 30000는 30초 건너뛰기를 의미한다.
  • transitionend : CSS 전이 효과가 완료되었을 때, 발생한다.
    성능 상 JS <<< CSS 이기 때문에, CSS를 이용할 수 있으면 이용하는 것이 좋다.
    공식 문서 : transition || W3School

😀 최종 완성본

💗 최종 코드

<!DOCTYPE html>
<html lang="ko">
  <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>[Javascript30] Drum-Kit</title>
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Black+Han+Sans&family=Hubballi&family=Nanum+Gothic+Coding&family=PT+Sans+Narrow:wght@700&family=Smooch+Sans&display=swap"
      rel="stylesheet"
    />
    <link rel="stylesheet" href="style drumKit.css" />
  </head>
  <body>
    <section class="keys">
      <section data-key="65" class="key">
        <kbd>A</kbd>
        <span class="sound">clap</span>
      </section>
      <section data-key="83" class="key">
        <kbd>S</kbd>
        <span class="sound">snare</span>
      </section>
      <section data-key="68" class="key">
        <kbd>D</kbd>
        <span class="sound">kick</span>
      </section>
      <section data-key="70" class="key">
        <kbd>F</kbd>
        <span class="sound">hihat</span>
      </section>
      <section data-key="71" class="key">
        <kbd>G</kbd>
        <span class="sound">bassDrum</span>
      </section>
      <section data-key="72" class="key">
        <kbd>H</kbd>
        <span class="sound">tom</span>
      </section>
      <section data-key="74" class="key">
        <kbd>J</kbd>
        <span class="sound">bottleTink</span>
      </section>
      <section data-key="75" class="key">
        <kbd>K</kbd>
        <span class="sound">ride</span>
      </section>
      <section data-key="76" class="key">
        <kbd>L</kbd>
        <span class="sound">boom</span>
      </section>
    </section>

    <!-- key값과 sound를 연결시켜주었다. -->
    <audio data-key="65" src="clap.wav"></audio>
    <audio data-key="83" src="snare.wav"></audio>
    <audio data-key="68" src="kick.wav"></audio>
    <audio data-key="70" src="hihat.wav"></audio>
    <audio data-key="71" src="bassDrum.wav"></audio>
    <audio data-key="72" src="tom.wav"></audio>
    <audio data-key="74" src="bottleTink.wav"></audio>
    <audio data-key="75" src="ride.wav"></audio>
    <audio data-key="76" src="boom.wav"></audio>

    <script src="app drumKit.js" defer></script>
  </body>
</html>
@import "reset.css";

body {
  background: no-repeat url("edm.jpg");
  background-size: cover;
  font-family: "Black Han Sans", sans-serif;
  font-family: "Hubballi", cursive;
  font-family: "Nanum Gothic Coding", monospace;
  font-family: "PT Sans Narrow", sans-serif;
  font-family: "Smooch Sans", sans-serif;
  font-size: 80px;
}

.keys {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  height: 100vh; /* 부모 태그와 관계없이 Viewpoint Height의 100%를 사용하겠다는 의미이다. */
  color: white;
}

.key {
  padding: 30px;
  border: 3px solid #5d636c;
  margin: 20px;
  border-radius: 5px;
  font-weight: bold;
  text-align: center;
  text-shadow: 0px 0px 5px black;
  transition: all 0.07s ease; /* 가장 중요한 부분이다. */
}

span.sound {
  font-size: 20px;
  text-align: center;
  color: black;
  font-weight: bold;
  text-shadow: 0px 0px 5px white;
}

.playing {
  transform: scale(1.1); /* 1.1배를 키울 것이다. */
  border: 5px solid #f6bb43;
  box-shadow: 0px 0px 10px #fffa1b;
}
window.addEventListener("keydown", playSound);

// console.log(event.keyCode);
// data 속성에 접근하기.
// console.log(audio);
// app drumKit.js:4 Uncaught DOMException: Failed to execute 'querySelector' on 'Document': 'audio[data-key=65]' is not a valid selector.
// 따옴표를 붙여주면 그런 Error가 등장하지 않는다.
// no audio === null

function playSound(event) {
  const audio = document.querySelector(`audio[data-key="${event.keyCode}"]`); // 따옴표가 중요하다.
  const key = document.querySelector(`section[data-key="${event.keyCode}"]`); // 오타가 진짜 중요하다. ㅈㅂ 오타오타.... 조심하자...

  if (!audio) {
    return; // stop the function from running all together.
    // 되감기를 해서 연속적으로 연타가 가능하도록 만들어준다.
  } else {
    audio.currentTime = 0; // rewind to the start.
    audio.play();
    // console.log(key);
    key.classList.add("playing"); // 왜 안 돌아가니..? -> 오타...
  }
}

// 실제 해당 함수를 만들어야 한다.
function removeTransition(event) {
  // console.log(event);
  if (event.propertyName !== "transform") {
    return;
  } else {
    // console.log(event.propertyName); // transform 이라는 단어를 기록하고 있다.
    // console.log(this);
    // 여기서 this는 key를 가르킨다.
    this.classList.remove("playing");
  }
}

// const keys = Array.from(document.querySelectorAll('.key'));
// Array.from 이용해서 하나씩 가져오기.
const keys = document.querySelectorAll(".key");
keys.forEach((key) => key.addEventListener("transitionend", removeTransition));
// 각각의 key 이벤트 리스너가 추가되어, 전환이 종료될 때 removeTransition 함수가 실행된다.

😀 마무리...

어우... 어렵다...;;
이것을 이용해서 전자 키보드를 만들어보고 싶다..!

profile
알고리즘과 웹 개발과 데이터 과학을 공부하는 대학생

0개의 댓글