挑戰 JS30 #1 - JavaScript Drum Kit

閱讀時間約 7 分鐘

這系列文章會記錄 JS30 當次挑戰時使用到的語法和相關知識。

JS30 官方有提供所有製作網頁的資源,不需要任何事前準備,就能無痛開始撰寫 JS,寫完之後還有 JS30 作者提供的解答,不知道怎麼下手時可以參考、寫完之後也能了解更多解法,改善自己的寫法。

本次的挑戰是「JavaScript Drum Kit」,需要達成的目標是:

「使用者按下鍵盤特定按鍵,按鍵會發光、同時播放相對應的音效。」

根據上面這個目標,大略知道該挑戰牽涉到以下面向:

  • 事件綁定、鍵盤事件
  • 流程控制
  • 操作音效物件
  • DOM 操作 - 包括樣式修改、取得 DOM 元素等

嘗試拆解流程

流程圖

流程圖

這個挑戰有個小細節,就是每次按下鍵盤後,都必須將音效時間軸歸零,這樣在每次觸發時才會從頭開始播放音效。

知識點

音效物件

網頁中引入音效可以使用 <audio> 元素,原來 audio 本身有 API 可以進行操作,相當方便,以下就來了解看看吧。

<audio> 標籤

<audio​ src="filePath"></audio>

常見屬性

  • autoplay:自動播放音檔
  • controls:出現控制條
  • loop:循環播放

有看到一些文章會建議搭配 source 標籤,避免某些音檔格式瀏覽器不支援,而發生預期外的效果。如果 source 的第一個格式不支援,就會向下讀取其他項目,通常 mp3 會是最普及的格式,可以放在最後。

<audio autoplay>
<source src="xxx.ogg">
<source src="xxx.mp3">
你的瀏覽器不支援 HTML5 音訊格式。
</audio>

audio 相關 API

const audio = document.querySelector("audio")

//開始播放​
audio.play();

//暫停播放​
audio.pause();

//將音檔秒數歸零​
//audio 物件還有許多屬性,幾乎完全可以透過 JS 控制 audio
audio.currentTime = 0;

<kbd> 標籤

語意標籤,表示「鍵盤輸入按鍵」,預設樣式就會將字型變為等寬字(monospace)

附上此次實作結果

更好的寫法

參考作者的寫法,發現在取得 DOM 元素時就先篩選出符合的元素,但筆者在這邊都使用 forEach 迴圈做篩選。

下方比較兩者寫法,可以看出筆者寫法較為冗長,可讀性較低,且跑迴圈會佔較多效能。

  • 筆者的寫法
const keys = document.querySelectorAll(".key");

const filter = function (list, userKeyboard) {
let el;
list.forEach((item) => {
if (item.dataset.key === userKeyboard) {
el = item;
}
});
return el;
};

const playAudio = function (e) {
const audios = document.querySelectorAll("audio");
const userKeyboard = String(e.keyCode);
if (filter(keys, userKeyboard)) {
filter(keys, userKeyboard).classList.add("playing");
filter(audios, userKeyboard).currentTime = 0;
filter(audios, userKeyboard).play();
}
};
  • 作者的寫法
function playSound(e) {
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
const key = document.querySelector(`div[data-key="${e.keyCode}"]`);
console.log(e.keyCode);
if (!audio) return;

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


增進使用體驗

製作完成後,發現有以下幾點可以做調整:

  1. 目前重複按下同一個鍵盤按鍵時,網頁按鈕樣式不會重複觸發,會一直亮著,雖然不影響使用,但希望有更細緻的體驗
  2. 如果完成第 1 點,會發現若鍵盤事件使用 keydown,當使用者按著按鍵不放時,事件會連續觸發,到某個程度時按鈕樣式就不再變動(簡言之「看起來」就像壞掉了XD);若鍵盤事件使用 keyup,雖然解決了事件重複觸發的問題,但筆者認為使用起來不符合預期效果,一般使用者應該會預期按下去的瞬間就發出音效,變成放開按鍵才發出音效似乎不夠即時

實作

一、重複點擊按鈕時,每次都會改變按鈕樣式

這部分是參考作者提供的解法,是使用transitionend 事件,監聽帶有 transition 屬性的元素,當 transitioned 結束時會觸發事件,有兩個情況下無法觸發該事件:

  • 在 transition 觸發以前元素就被設定 display: none
  • 有修改目標節點的 transition-property 屬性
function removeTransition(e) {
console.log(e.propertyName);
if (e.propertyName !== "transform") return;
e.target.classList.remove("playing");
}

作者是透過 propertyName 找到 transform 屬性,但似乎會導致重複觸發時按鈕樣式就不會再變動,不確定原因為何,目前想到的解決方式可往下看第二點。

二、在使用 keydown 的情況下,可以連續觸發樣式,且保持運作正常

解法1: 直接在 keyup 事件上綁定「清除按鈕樣式」的處理器,有點像買保險的感覺XD

let keys = document.querySelectorAll(".key");
const removeSound = function (e) {
keys.forEach((item) => {
item.classList.remove("playing");
});
};
window.addEventListener("keyup", removeSound);

解法2: transitionend 事件的 propertyName,找到除了 transform 以外的其他屬性,就能正常運作,這部分原因尚待釐清。

目前想到的解法是這樣,如果有更好的再補上。

題外話

發現下載後的檔案都有 favicon(瀏覽器頁籤會顯示的 icon),意外發現這個網站,可以自行替換掉後面的表情符號,蠻有趣的XD

今天就介紹到這裡,若有錯誤歡迎指正,也歡迎大家分享自己的看法。

參考資料

avatar-img
7會員
30內容數
正在一點一滴學習程式,相信知識量總有一天會匯聚成大海,目前專門研究前端中。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
你可能也想看
Google News 追蹤
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
學習混音可以分為幾個步驟,即使你沒有經驗,也能逐步掌握這門技術
Thumbnail
這篇文章是來響應跳鼠飛行日記的30天歌單挑戰 Day1-Day5,搜集了以金屬樂為主,我常聽的工作與創作用音樂。 其實我聽的金屬樂非常偏科,大量集中在主唱是女性的樂團上,不過為了顧及推薦的多樣性,還是會將我常聽且知名度高的樂團包含在名單內。 這個小系列會包括三十首歌,每篇十首,那就開始吧!
Thumbnail
看到咚咚老師30天歌單挑戰跟彼得老師30天歌單挑戰,覺得非常有趣,也想來分享一下我平常會聽的歌曲,我會分成六篇文章來po,順便湊文章數(真是計畫通~)。 另外也想用平常聽的音樂類別給自己一個挑戰。
Thumbnail
JavaScript30 傳送門:https://javascript30.com/ 寫到挑戰六覺得心累ㄌ,向來不是一個可以長久堅持好習慣的人,30 個挑戰聽起來很少,但如果要日復一日堅持下去其實好長r 😮‍💨 挑戰六透過 input 來 filter 從 api 拿回來的資料結
Thumbnail
JavaScript30 傳送門:https://javascript30.com/ Console.table 第四個練習是透過一些 array method 來操作 array 資料。 整個練習最讓我 WOW 的,是 console.table,竟然有這麼酷的 console
Thumbnail
JavaScript30 傳送門:https://javascript30.com/ 透過 JS 控制 CSS 變數 今天的挑戰是要能透過滑動圖片上面的 <input type="range"> 來改變圖片的三個屬性。 以前在 CSS 裡面看到 var(--xxxx) 這種東西都很
Thumbnail
JavaScript30 傳送門:https://javascript30.com/ 今天才發現可以不用看著影片一步一步寫,可以自己先寫寫看😛 第二個挑戰是用 JS + CSS 寫一個時鐘,不過其實檔案裡面 HTML 跟 CSS 都已經寫好了,三個指針也都放好了,Go Live 起來就長
Thumbnail
想要找個地方記錄自己寫 JavaScript 30 挑戰時額外的小筆記✍🏻 JavaScript30 傳送門:https://javascript30.com/ 第一個挑戰是寫一個 keydown 事件並讓頁面隨著不同的按鍵發出不同的聲音。
Thumbnail
最近在練習吉他的彈奏,靈機一動,對著電視就練了起來。 原來我把電視轉到YouTube,找一些曾經聽過的懷舊老歌,先找到它的單音,再找它的調門和弦,最後就跟著一起彈奏起來,時不時還跟著唱。 這其實是我心目中最理想的「卡拉OK」,號稱「純人工手動的吉他卡拉OK」,因為這樣的音樂境界在我心目中才算OK
Thumbnail
本文分享了作者初學樂器時的試煉,如何突破與學習樂器時遇到的困難。作者講述了從音樂基礎學習的重要性,並提到了一個Youtuber的節奏練習影片推薦。
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
學習混音可以分為幾個步驟,即使你沒有經驗,也能逐步掌握這門技術
Thumbnail
這篇文章是來響應跳鼠飛行日記的30天歌單挑戰 Day1-Day5,搜集了以金屬樂為主,我常聽的工作與創作用音樂。 其實我聽的金屬樂非常偏科,大量集中在主唱是女性的樂團上,不過為了顧及推薦的多樣性,還是會將我常聽且知名度高的樂團包含在名單內。 這個小系列會包括三十首歌,每篇十首,那就開始吧!
Thumbnail
看到咚咚老師30天歌單挑戰跟彼得老師30天歌單挑戰,覺得非常有趣,也想來分享一下我平常會聽的歌曲,我會分成六篇文章來po,順便湊文章數(真是計畫通~)。 另外也想用平常聽的音樂類別給自己一個挑戰。
Thumbnail
JavaScript30 傳送門:https://javascript30.com/ 寫到挑戰六覺得心累ㄌ,向來不是一個可以長久堅持好習慣的人,30 個挑戰聽起來很少,但如果要日復一日堅持下去其實好長r 😮‍💨 挑戰六透過 input 來 filter 從 api 拿回來的資料結
Thumbnail
JavaScript30 傳送門:https://javascript30.com/ Console.table 第四個練習是透過一些 array method 來操作 array 資料。 整個練習最讓我 WOW 的,是 console.table,竟然有這麼酷的 console
Thumbnail
JavaScript30 傳送門:https://javascript30.com/ 透過 JS 控制 CSS 變數 今天的挑戰是要能透過滑動圖片上面的 <input type="range"> 來改變圖片的三個屬性。 以前在 CSS 裡面看到 var(--xxxx) 這種東西都很
Thumbnail
JavaScript30 傳送門:https://javascript30.com/ 今天才發現可以不用看著影片一步一步寫,可以自己先寫寫看😛 第二個挑戰是用 JS + CSS 寫一個時鐘,不過其實檔案裡面 HTML 跟 CSS 都已經寫好了,三個指針也都放好了,Go Live 起來就長
Thumbnail
想要找個地方記錄自己寫 JavaScript 30 挑戰時額外的小筆記✍🏻 JavaScript30 傳送門:https://javascript30.com/ 第一個挑戰是寫一個 keydown 事件並讓頁面隨著不同的按鍵發出不同的聲音。
Thumbnail
最近在練習吉他的彈奏,靈機一動,對著電視就練了起來。 原來我把電視轉到YouTube,找一些曾經聽過的懷舊老歌,先找到它的單音,再找它的調門和弦,最後就跟著一起彈奏起來,時不時還跟著唱。 這其實是我心目中最理想的「卡拉OK」,號稱「純人工手動的吉他卡拉OK」,因為這樣的音樂境界在我心目中才算OK
Thumbnail
本文分享了作者初學樂器時的試煉,如何突破與學習樂器時遇到的困難。作者講述了從音樂基礎學習的重要性,並提到了一個Youtuber的節奏練習影片推薦。