有開頭總是好事,沒開頭就什麼事也不會發生。
這個練習是在畫面上顯示好幾個鍵盤上面的鍵,讓使用者在按下鍵盤,依鍵盤不同而發出不同的聲響,原本的練習是以英文字母為發音鍵,但自己愛好自然就把它們換成了數字鍵,並搭配各種不同動物的叫聲也頗有趣的。
如果依照「功能」和「畫面」來拆解這個練習,那麼也表示「功能的部分」需要用 DOM 和 JavaScript 來操控,而「畫面」部分則需要先用 HTML + CSS 來刻好,以 JS 來等待觸發事件,來播放音效與變換元件(鍵)的狀態。
功能: 按下鍵時,觸發相對應的聲音。離開鍵時聲音消失。
畫面: 按下實際鍵時,畫面上對應的鍵會有反應,搭配動畫效果。離開自下去的鍵時會恢復原狀。
- 如何監聽到按到哪個鍵: -> 監聽 keydown 事件
- 鍵在按下去的時候播放音效: -> 呼叫播放音效的函式 -> playSound()
- 鍵在按下去的同時,播放動畫效果 CSS: ->
key.classList.add('playing');
- 鍵在按下去之後恢復原狀: -> 呼叫
removeTransition(event)
執行event.target.classList.remove('playing');
作切換。
HTML 的部分
- 依序將需要的鍵顯示出來:
<kbd>7</kbd>
表示鍵入數字 7 - 使用
data-
這個屬性來放入自定義的資料,這裡我們用data-key
來定義。
1 | <div data-key="55" class="key"> |
再以data-key
將我們會使用到的audio
音效關聯起來。
1 | <audio data-key="55" src="sounds/loup.wav"></audio> |
關於 data-
屬性
data-
屬性可以讓我們自定義資料,它可以讓我們在任意的html
標籤上放入我們自定義的資料屬性,
要注意的是,屬性名不可以是大寫字母,前面需要加上前綴詞data-
緊接至少一個小寫字符,如我們用的key
成了我們自定義的data-key
標籤,例如<audio data-key="55">
CSS 的部分
- 我們先把要出現的效果寫好,命名為
.key
樣式。 - 當我們按下按鍵時,加上
.playing
樣式 讓畫面上的按鍵有些效果。這個.playing
樣式會在離開這個鍵時被拿掉。
1 | .key { |
JavaScript 的部分
前面有提到,當我們按下按鍵,程式會去呼叫發出音效的playSound()
函式,做到這一點,我們得先監聽這些按鍵,何時被按下繼而觸發事件。
我們先將所有的按鍵放在一個陣列型別的變數keys
裡,然後利用forEach()
走訪陣列裡的元素(鍵),並監聽所會發生的事件,給予事件一個名稱:transitionend
,並在觸發時,執行removeTransition
函式去清除按下去被觸發的行為。也就是使鍵盤的 Key 恢復還沒被按下去的原狀。
1 | const keys = Array.from(document.querySelectorAll('.key')); |
接著我們來看看發出音效的函式playSound(event)
:
- 利用
querySelector
先找出「音效」和「鍵」的 DOM 元素。 - 如果沒有找到
audio
就什麼也不做。 - 在
key
上面加上動畫效果的playing
樣式。 - 讓播放音效的時間歸零。
- 再開始播放。
1 | function playSound(event) { |
按鍵的監聽+音效的播放+按下鍵的動畫效果
還有,在按下按鍵發出聲效後,使鍵盤的 Key 恢復原狀,移除這個動畫效果的removeTransition(event)
函式:
- 先檢查這個要監聽的事件元素,如果沒有 CSS 的動畫屬性
transform
就什麼都不做。 - 否則,就在這個元素上移除這個
.playing
CSS 樣式。
1 | function removeTransition(event) { |
需要注意的是,我們必須把寫好的函式放在 JS 程式碼的最前面,因為程式是一行行讀的,如果沒有先放前面,在讀到呼叫這些函式時,程式會因為還沒記憶這些函式而出現錯誤。
相關語法
在這個練習裡的JavaScript
,我們用到了一些 ES6 的語法:
- 使用了
const
的變數宣告,表示常數只能賦值一次。也就是說,賦值給這個變數後,值就不能再更改了。 - 使用了字串模板(Template literals),語法為:
字串 ${變數、屬性名}
,樣板字面值 MDN forEach
和箭頭函式:因為在這個練習裡使用的是document.querySelector
來得到一組 NodeList(DOM),使用forEach
方法來走訪這些 DOM 元素。
基礎語法
- 使用EventTarget.addEventListener()來增加監聽事件。
- 使用 document.querySelector 來抓去我們所要監聽的 DOM 元素。
解決問題
如何可以讓我們按下鍵盤某個鍵時,和音效連結起來? 可以連結的原因是在keydown
的事件中的keyCode
屬性,keyCode
的屬性值和 ASCII
的編碼相同,我們可以在 JavaScript Event KeyCodes 查找對應的鍵碼。
我們在按鍵的div
和音效的audio
標籤中,都增加了一個我們自定義的屬性data-key
,這樣我們在按下按鍵的同時,也可依循找到對應的音效。
如何讓按鍵即使按著不放,也可以馬上有連續的音效? 我們把每次要播放音效之前,將播放的時間點歸零。
如何讓每次按完鍵後,恢復成原狀? 在監聽按鍵時,我們使用transitionend
這個事件,讓它在動畫效果結束後被觸發,動畫效果完成之後,就去除這個樣式。但是因為按鍵按下時,不只只有動畫效果transform
所以需要增加一個if
的判斷式,讓每發生一次按鍵事件,只去除一次樣式。
碎碎唸:其實如果沒有這個判斷式,如果按住按鈕不放,也不會有一直是有動畫效果停滯,反而效果較好。
後記
雖然看似步驟不複雜的按鍵與音效,要注意的部分也不少,希望在這樣的詳細拆解之下,可以多了解不同語法的應用與使用的原因,以實務的方式了解程式面,的確比較類比多了。