JS30-01-JavaScript-Drum-Kit

使用鍵盤事件,觸發聲音及特效

目標

  • 按下鍵盤時,播放聲音並顯示 CSS 效果。
  • 播放完畢後,移除 CSS 效果。

實作要點

  1. 利用 JS addEventListener 監聽鍵盤的keydown Event

    • 由按下鍵盤的 keyCode 選取出相對應的的 audio標籤 和 div標籤
    • audio 標籤 → 取得音效資源播放聲音
    • div 標籤 → 套用 CSS 效果
  2. 監聽 CSS 的 transitionend Event (transitionend 事件會在 transition 结束之後觸發)

    • e.propertyName !== "transform" 僅針對 transform 繼續做事,不是則停止
    • 若為 transform,則移除 CSS 效果

成品展示

[CodePen]


JS 學習紀錄

HTML5 標籤 HTMLMediaElement

透過 js 取得 HTMLMediaElement 元素,來進行影音的播放。

HTML
1
<audio data-key="65" src="sounds/clap.wav"></audio>
JS
1
2
3
4
5
6
//取得 HTMLMediaElement 元素
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`); //ES6
if (audio) {
audio.currentTime = 0; //持續撥放,設置或返回媒體中的當前播放位置(以秒 Seconds 為單位)
audio.play(); // 播放音效
}

DOM 元素 Element.classList

透過 classList 新增、移除、切換 CSS 屬性,同等於 jQuery 的 addClassremoveClass

HTML
1
2
3
4
<div data-key="65" class="key">
<kbd>A</kbd>
<span class="sound">clap</span>
</div>
JS
1
2
3
4
5
6
//取得 DOM 元素
const dom = document.querySelector(`div[data-key="${e.keyCode}"]`); //ES6

if (dom) key.classList.add('playing'); //新增CSS屬性
key.classList.remove('playing'); //移除CSS屬性
key.classList.toggle('playing'); //切換CSS屬性

DOM 元素 NodeList

使用 querySelectorAll 取得的 DOM 是 NodeList 型態,非 Array 型態,
雖然 也可以使用 NodeList.prototype.forEach() 的 method,但有可能會遇到部份瀏覽器不支援,所以一般來說為了相容性,會先轉換成 Array 型態再拿來做使用。

HTML
1
2
3
4
5
6
7
8
<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>
JS
1
2
3
4
5
// querySelectorAll 取得 DOM 元素 → NodeList 型態
const keys = document.querySelectorAll('.key');

// NodeList.prototype.forEach() → IE不支援
keys.forEach(key => key.addEventListener('transitionend', transitionEndHandler));

可藉由下列方法,將 NodeList 轉換 Array 型態,支援度較高。

NodeList 轉換 Array
1
2
3
4
5
// Array.from() 轉Array型態
const keys = Array.from(document.querySelectorAll('.key'));

// 轉換成 Array型態 後,就可以使用 forEach()、map(), concat() …等method
keys.forEach(key => key.addEventListener('transitionend', transitionEndHandler));

也可以使用 ES6 … 展開運算子(Spread Operator)

JS 展開運算子
1
2
3
// ...展開運算子
const keys = [...document.querySelectorAll('.key'))];
keys.forEach(key => key.addEventListener('transitionend',transitionEndHandler))

監聽 CSS transitionend 事件

transitionend 事件會在 CSS transition 結束後觸發

JS
1
2
3
4
5
6
7
8
function transitionEndHandler(e) {
//因為CSS class [.key]做了transition: all,會使多個CSS效果同時執行。
//監聽transitionend時會發現觸發了很多次,ex:border-left-color、border-right-color、transform...
//我們只需要對transform事件做處理
if (e.propertyName == 'transform') {
e.currentTarget.classList.remove('playing'); //currentTarget ==> 事件綁定的對象 或用Target也行。
}
}