- Published on
JS30 - 01 Drum Kit
Drum Kit 挑戰是當按下鍵盤按鍵時,會發出不同的鼓聲音效
- 透過
keydown
事件監聽鍵盤按鍵 - 透過事件代理與物件儲存,減少不必要的重複查詢,提升效能
監聽鍵盤事件
監聽 keydown
事件,當按下鍵盤按鍵時,會觸發 playSound
事件
window.addEventListener('keydown', playSound)
儲存 audio 和 key 元素
使用物件來儲存每個按鍵與其對應的音效元素,避免重複查詢 DOM,提升效能
const audioElements = {}
const keyElements = {}
const keys = document.querySelectorAll('.key')
keys.forEach((key) => {
keyElements[key.dataset.key] = key
})
const audios = document.querySelectorAll('audio[data-key]')
audios.forEach((audio) => {
audioElements[audio.dataset.key] = audio
})
- 透過
dataset.key
取得每個元素的data-key
屬性,並將其對應的key
和audio
元素儲存到物件中
播放音效並加上動畫效果
當觸發 keydown
事件時,會根據按鍵碼(e.code
)取得對應的音效與按鍵元素,播放音效並加上 .playing
類別來啟動動畫效果
const playSound = (e) => {
const audio = audioElements[e.code]
const key = keyElements[e.code]
if (!audio || !key) return
audio.currentTime = 0
audio.play()
key.classList.add('playing')
}
e.code
對應每個按鍵,並播放對應的音效- 原本教學使用
e.keyCode
,但官網表示這個屬性已經 deprecated,所以改用e.code
來取得按鍵碼
- 原本教學使用
audio.currentTime = 0
確保音效可以從頭開始播放
移除動畫效果
當過渡動畫結束時,我們需要移除 .playing
類別,使按鍵動畫恢復初始狀態
const removeTransition = (e) => {
if (e.propertyName !== 'transform') return
e.target.classList.remove('playing')
}
- 因為
transform
結束較晚,所以只要transform
結束就可以移除.playing
類別
監聽過渡動畫結束事件
原本的寫法是為每個 .key
元素分別綁定 transitionend
事件
keys.forEach((key) => {
key.addEventListener('transitionend', removeTransition)
})
但可以改為使用事件代理(Event Delegation),來對 .keys
父元素統一監聽,這樣不僅能減少監聽器的數量,還能提高效能
document.querySelector('.keys').addEventListener('transitionend', removeTransition)
- 當任何子元素的
transition
結束時,事件會冒泡到父元素.keys
,觸發removeTransition
事件