Home JavaScript30 - Day 1 Drum kit
Post
Cancel

JavaScript30 - Day 1 Drum kit

JavaScript30

전부터 자바스크립트를 배우고 싶었는데 프레임워크, 컴파일러, 라이브러리가 적용되지 않은 Vanilla JS로 30개의 프로젝트를 만드는 Wes Bos의 강의를 발견하였다. 마침 첫 프로젝트부터 내가 관심있었던 Audio를 페이지 내 player로 적용해볼 수 있어서 시작하게 되었다.

Wes BosJavaScript30 Challenge유튜브깃헙 레포지토리에서 확인할 수 있다. 나는 해당 레포지토리는 fork해서 코드를 받아 진행했다.

Day 1 - JavaScript30 Drum kit

START

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>JS Drum Kit</title>
  <link rel="stylesheet" href="style.css">
  <link rel="icon" href="https://fav.farm/🔥" />
</head>
<body>


  <div class="keys">
    <div data-key="65" class="key">
      <kbd>A</kbd>
      <span class="sound">clap</span>
    </div>
    <div data-key="83" class="key">
      <kbd>S</kbd>
      <span class="sound">hihat</span>
    </div>
    <div data-key="68" class="key">
      <kbd>D</kbd>
      <span class="sound">kick</span>
    </div>
    <div data-key="70" class="key">
      <kbd>F</kbd>
      <span class="sound">openhat</span>
    </div>
    <div data-key="71" class="key">
      <kbd>G</kbd>
      <span class="sound">boom</span>
    </div>
    <div data-key="72" class="key">
      <kbd>H</kbd>
      <span class="sound">ride</span>
    </div>
    <div data-key="74" class="key">
      <kbd>J</kbd>
      <span class="sound">snare</span>
    </div>
    <div data-key="75" class="key">
      <kbd>K</kbd>
      <span class="sound">tom</span>
    </div>
    <div data-key="76" class="key">
      <kbd>L</kbd>
      <span class="sound">tink</span>
    </div>
  </div>

  <audio data-key="65" src="sounds/clap.wav"></audio>
  <audio data-key="83" src="sounds/hihat.wav"></audio>
  <audio data-key="68" src="sounds/kick.wav"></audio>
  <audio data-key="70" src="sounds/openhat.wav"></audio>
  <audio data-key="71" src="sounds/boom.wav"></audio>
  <audio data-key="72" src="sounds/ride.wav"></audio>
  <audio data-key="74" src="sounds/snare.wav"></audio>
  <audio data-key="75" src="sounds/tom.wav"></audio>
  <audio data-key="76" src="sounds/tink.wav"></audio>

<script>

</script>


</body>
</html>

PROCESS

1. 키보드를 누르면 키보드 이벤트가 발생한다.

1
2
3
4
5
<script>
    window.addEventListener('keydown', function(e) {
        console.log(e)
    });
</script>

2. 키보드 이벤트의 key code를 출력할 수도 있다.

1
2
3
4
5
<script>
    window.addEventListener('keydown', function(e) {
        console.log(e.keyCode);
    });
</script>
keykeycode
a65
s83
d68
f70
g71

3. keycode와 같은 audio의 data-key를 불러온다.

1
2
3
4
5
6
<script>
    window.addEventListener('keydown', function(e) {
        const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
        console.log(audio);
    });
</script>

<audio data-key=”65” src=”sounds/clap.wav”></audio>
<audio data-key=”83” src=”sounds/hihat.wav”></audio>
<audio data-key=”68” src=”sounds/kick.wav”></audio>
<audio data-key=”70” src=”sounds/openhat.wav”></audio>
<audio data-key=”71” src=”sounds/boom.wav”></audio>

4. audio의 play 기능을 활성화한다.

키보드의 키를 연속해서 누르면 사운드의 반응 속도가 느리다. openhat.wav 오디오 소스는 1초 정도 되는데 끝날 때까지 중간에 연속 재생이 안된다. 따라서, audio.currentTime = 0으로 시작 지점을 되감는다.

1
2
3
4
5
6
7
8
<script>
    window.addEventListener('keydown', function(e) {
        const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
        if (!audio) return;  // stop the funtion from running all together
        audio.currentTime = 0;  // rewind to the start
        audio.play();
    });
</script>

5. 스타일 조작을 위해 keycode와 동일한 key 클래스의 data-key를 불러온다.

1
2
3
4
5
6
7
8
9
10
<script>
    window.addEventListener('keydown', function(e) {
        const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
        const key = document.querySelector(`.key[data-key="${e.keyCode}"]`);
        if (!audio) return;  // stop the funtion from running all together
        audio.currentTime = 0;  // rewind to the start
        audio.play();
        console.log(key);
    });
</script>

6. classList를 사용해 CSS의 .playing을 불러오고 웹 페이지의 스타일을 조작한다.

key를 누르면 .playing의 스타일이 적용되지만 오디오가 끝나도 여젼히 스타일이 적용되어있다.

1
2
3
4
5
.playing {
  transform: scale(1.1);
  border-color: #ffc600;
  box-shadow: 0 0 1rem #ffc600;
}
1
2
3
4
5
6
7
8
9
10
<script>
    window.addEventListener('keydown', function(e) {
        const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
        const key = document.querySelector(`.key[data-key="${e.keyCode}"]`);
        if (!audio) return;  // stop the funtion from running all together
        audio.currentTime = 0;  // rewind to the start
        audio.play();
        key.classList.add('playing');
    });
</script>

7. key를 누르면 transition event를 만든다.

1
2
3
4
5
6
7
8
9
<script>
    ...
    function removeTransition(e) {
        console.log(e);
    };

    const keys = document.querySelectorAll('.key');
    keys.forEach(key => key.addEventListener('transitionend', removeTransition));
</script>

8. 전환 종료에 대한 기능을 세부적으로 지정한다.

1
2
3
4
5
6
7
8
9
10
<script>
    ...
    function removeTransition(e) {
        if (e.propertyName !== 'transform') return; // skip it if it's not a transform
        this.classList.remove('playing');
    };

    const keys = document.querySelectorAll('.key');
    keys.forEach(key => key.addEventListener('transitionend', removeTransition));
</script>

FINISHED

깃헙 레포지토리에서는 finished.html이 리펙토링 되어있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>JS Drum Kit</title>
  <link rel="stylesheet" href="style.css">
  <link rel="icon" href="https://fav.farm/✅" />
</head>
<body>


  <div class="keys">
    <div data-key="65" class="key">
      <kbd>A</kbd>
      <span class="sound">clap</span>
    </div>
    <div data-key="83" class="key">
      <kbd>S</kbd>
      <span class="sound">hihat</span>
    </div>
    <div data-key="68" class="key">
      <kbd>D</kbd>
      <span class="sound">kick</span>
    </div>
    <div data-key="70" class="key">
      <kbd>F</kbd>
      <span class="sound">openhat</span>
    </div>
    <div data-key="71" class="key">
      <kbd>G</kbd>
      <span class="sound">boom</span>
    </div>
    <div data-key="72" class="key">
      <kbd>H</kbd>
      <span class="sound">ride</span>
    </div>
    <div data-key="74" class="key">
      <kbd>J</kbd>
      <span class="sound">snare</span>
    </div>
    <div data-key="75" class="key">
      <kbd>K</kbd>
      <span class="sound">tom</span>
    </div>
    <div data-key="76" class="key">
      <kbd>L</kbd>
      <span class="sound">tink</span>
    </div>
  </div>

  <audio data-key="65" src="sounds/clap.wav"></audio>
  <audio data-key="83" src="sounds/hihat.wav"></audio>
  <audio data-key="68" src="sounds/kick.wav"></audio>
  <audio data-key="70" src="sounds/openhat.wav"></audio>
  <audio data-key="71" src="sounds/boom.wav"></audio>
  <audio data-key="72" src="sounds/ride.wav"></audio>
  <audio data-key="74" src="sounds/snare.wav"></audio>
  <audio data-key="75" src="sounds/tom.wav"></audio>
  <audio data-key="76" src="sounds/tink.wav"></audio>

<script>
  function removeTransition(e) {
    if (e.propertyName !== 'transform') return;
    e.target.classList.remove('playing');
  }

  function playSound(e) {
    const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
    const key = document.querySelector(`div[data-key="${e.keyCode}"]`);
    if (!audio) return;

    key.classList.add('playing');
    audio.currentTime = 0;
    audio.play();
  }

  const keys = Array.from(document.querySelectorAll('.key'));
  keys.forEach(key => key.addEventListener('transitionend', removeTransition));
  window.addEventListener('keydown', playSound);
</script>


</body>
</html>


Key Code

DrumKit1

키보드의 특정 키와 연결된 숫자로 data-key를 지정한다. 키보드의 A를 누르면 clap 사운드가 재생된다.

addEventListenerkeydown이라는 event는 키보드에서 지정된 키 중 하나를 눌렀을 때 키보드 이벤트가 발생한다. audio.currentTime = 0은 오디오를 반복 재생해준다. classList로 DOM(문서 객체 모델)에 add, remove, toggle을 사용하여 웹 페이지의 스타일을 조작한다.


Transition End Event

transitionend라는 event로 전환 종료에 대한 세부적인 기능을 만든다.

키보드를 누를 때 drum kit의 오디오 사운드가 들리는게 신기했다. 컴퓨터 비전 라이브러리인 OpenCV에서 키보드 이벤트인 cv2.waitKey(delay=None)나 마우스 콜백함수인 cv2.setMouseCallback(windowName, mouse_fn, param=None)와 비슷해보였다. JS는 익숙하지 않지만, 처음 Python을 배웠을 때처럼 차근차근 해봐야지

This post is licensed under CC BY 4.0 by the author.