JS30 為加拿大的 Wes Bos 工程師所建立的免費課程,課程會在三十天的時間帶領學生以原生的 HTML + CSS + JavaScript 語法,解決課堂上的 30 個前端開發小專案,以此來逐步建立對於前端語法的基本知識。
由於我英文稍差的關係 QwQ,本文將以 Alex 宅幹嘛 的 JS30 中文導讀為主要的學習方式,另外會採用 Web Bos 每堂課提供給我們的 初始文件,裡面有已經寫好基本 HTML + CSS 的檔案,只要再依照專案的要求加上進階的 CSS 和 JS 效果即可。
本文會盡量以文字的方式,紀錄這 30 天每一個專案的實作,希望在這 30 天的時間能讓自己更加的認識 JavaScript
第一天的目標是建置一個網頁上的架子鼓,隨著使用者按下鍵盤的按鍵,會發出各種不同音色的鼓、鈸聲,並在畫面上顯示按鍵的打擊特效(彈跳、發光)
Github 檔案位置:01 - JavaScript Drum Kit
網頁一開始的樣子如下,這時候網頁沒有任何的互動效果和動畫
可以先去看看 最後的成品
將程式的要求拆分步驟後,我們需要做的事情如下
在這裡我們會使用 window.addEventListener('事件', '觸發事件後執行的函式')
監聽行為
在鍵盤按鍵被按下後會觸發 keydown
事件,鬆開後會觸發 keyup
事件,在此希望可以在案下時就觸發,所以選擇使用 keydown
事件做偵測
第一步驟的 JS 程式如下
function playSound(){
console.log('按鍵事件監聽成功')
}
window.addEventListener('keydown', playSound);
在這裡我們會利用 const audio = document.querySelector('元素')
選取在 HTML 已有的 Audio 元素音檔,再以 audio.play()
播放音訊
在這時候我們可以在 playSound()
中加入 console.log()
,我們會在網頁的 Console 看到 KeyboardEvent,展開後會有密密麻麻的東西
function playSound(e){
console.log(e)
}
備註:也可以利用 這個網站 看 KeyboardEvent 的資訊
我們跳過大部分的無用資訊,下面有一個名為 keyCode
的數值,我們可以利用這個數值和 data-* 屬性結合應用,選取不同的音檔
function playSound(e){
console.log(e);
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
}
備註:括號裡的應用是 ES6 樣板字面值 -> ` ` 語法
我們可以利用 audio.play();
函式撥放音檔,另外要注意的細節是,如果使用者按下的 KeyCode 是不存在的元素和音檔,要即時 return
中止掉
function playSound(e){
//console.log(e);
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
if(!audio) return;
audio.play();
}
現在在按下按鍵後已經可以撥放音訊了,但美中不足的是無法做到音訊的連續撥放(連擊鼓聲),原因是在音訊撥放完之前,audio.play()
是不會有作用的。
因此我們可以加上 audio.currentTime = 0
初始化音檔,達成連續撥放
function playSound(e){
//console.log(e);
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
if(!audio) return;
audio.currentTime = 0;
audio.play();
}
在這裡我們一樣會利用 const key = document.querySelector('元素')
,搭配 KeyCode 選取在 HTML 已有的 div 元素,再以 key.classList.add('playing')
的方式,加上原始文件有的 CSS 屬性特效
function playSound(e){
//console.log(e);
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
const key = document.querySelector(`div[data-key="${e.keyCode}"]`);
if(!audio) return;
audio.currentTime = 0;
audio.play();
key.classList.add('playing');
}
到這裡的效果圖如下
我們最後的成品希望能藉由快速的加上和移除特效,模擬敲擊的過程
要移除按鍵的 .playing 可以利用 key.addEventListener()
監聽 'transitionend' 事件,鎖定移除目標,再藉由 e.currentTarget.classList.remove('playing')
移除
我們在這裡利用 key.forEach({})
的方式幫所有按鍵加上監聽
key.forEach(key => { // 與 for 迴圈類似,可以遍歷 key 物件中的元素
key.addEventListener('transitionend', removeTranstion);
})
一樣,先看看他給我們的資訊,由此可以知道每個 CSS 屬性都會有自己獨特的 "transitionend"
function removeTranstion(e){
console.log(e);
}
因此我們取 "transform" 為基準,並利用 e.currentTarget.classList.remove('playing')
達成移除目標
function removeTranstion(e){
// console.log(e); // 由此知道每個屬性都會有自己獨特的 "transitionend"
if(e.propertyName === 'transform'){ // 取 "transform" 為基準
e.currentTarget.classList.remove('playing'); // 在對應元素移除名為 "playing" 的 class
}
}
<script>
function playSound(e){
console.log(e); // 由此知道 e 代表的 keyboardEvent 的內容,本次會取 keyCode 做主要利用
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`); // 依照所按下的按鍵,選取對應的音檔
// console.log(audio);
if(audio){
audio.currentTime = 0; // 初始化音檔,達成連續播放
audio.play();
}
const key = document.querySelector(`div[data-key="${e.keyCode}"]`); // 依照所按下的按鍵,選取與之對應的元素 <div>
// console.log(key);
if(key){
key.classList.add('playing'); // 在對應的元素添加名為 "playing" 的 class
}
}
function removeTranstion(e){
// console.log(e); // 由此知道每個屬性都會有自己獨特的 "transitionend"
if(e.propertyName === 'transform'){ // 取 "transform" 為基準
e.currentTarget.classList.remove('playing'); // 在對應元素移除名為 "playing" 的 class
}
}
const key = document.querySelectorAll('.key'); // 選取所有包含 class => "key" 的元素
// console.log(key);
key.forEach(key => { // 與 for 迴圈類似,可以遍歷 key 物件中的元素
key.addEventListener('transitionend', removeTranstion); // 將所有元素增加 'transitionend' 事件的監聽
})
window.addEventListener('keydown', playSound); // 在網頁增加 'keydown' 事件的監聽
</script>
以上是第一天的製作紀錄,如有錯誤或不足的地方還請多多指教 >.<
JavaScript30
[ Alex 宅幹嘛 ] ?? 深入淺出 Javascript30 快速導覽:Day 1:JavaScript Drum Kit
MDN Web Docs - data-*
MDN Web Docs - Template_literals