挑戰 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

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

參考資料

6會員
30內容數
正在一點一滴學習程式,相信知識量總有一天會匯聚成大海,目前專門研究前端中。
留言0
查看全部
發表第一個留言支持創作者!
你可能也想看
挑戰七天日更 DAY 1 :圖書館和奶茶這次方格子辦了有趣的活動,要連寫七天的文章,這對超過三個月沒動筆的我來說是個挑戰。不過正因為如此,才要更勇於去嘗試,也會開始去反思,是否對於寫文章這件事過於慎重......
Thumbnail
avatar
Kevin Chen
2021-09-20
【挑戰-用50字寫一個故事】斷捨離 隨想曲我還可以用 不要丟棄我 不!要!啊! 我是裝飾擺設 你看不到我 你看不到我 我是你們的回憶 你不能遺棄我呀 對不起!感謝!再見! 最近又開始斷捨離,整理時,一邊檢視物品,一邊想像物品會對我說甚麼? 我想對它們說:對不起,我們要輕裝上路!感謝你們過去陪伴,再見!
Thumbnail
avatar
Emma
2021-01-05
【挑戰-用50字寫一個故事】折翼的天使魔鬼說 跳下去 所有痛苦絕望 消失殆盡 跳下去 世界不會因為你而停轉 折翼的天使說 信望愛 留下來 一起旁觀世界的運轉
Thumbnail
avatar
Emma
2021-01-04
挑戰島主徵選【1分鐘自介影片】腳本指南新媒體的崛起,不論是社群平台、Youtube、部落格,每個人都有一個讓他人認識自己的門戶,但,回到自我本身,有沒有想過,隨著時代的轉變,更多企業需要透過影音履歷來認識你!這麼多年來,你是否也未曾想過要用一分鐘時間來介紹自己呢?我也有過同樣的瓶頸,但,在這裡,我已經找到解法,就一起來實務操作吧!
Thumbnail
avatar
逆商教主Jinjin Wen
2020-12-03
挑戰NBA最強後衛 - Damian Lillard  先帶大家認識一下,2012 NBA選秀第1輪第6順位誕生出來的波特蘭拓荒者一哥Damian Lillard是當年賽季的新人王,賽季打不到一半,Lillard的名聲就開始響徹雲霄,先是取得技術挑戰的冠軍再破NBA新秀最多三分球紀錄,而後再以19分6.5助攻3.1籃板取得自己的新人王...
Thumbnail
avatar
韋恩
2020-08-31
挑戰不穿內衣的生活!5個不穿內衣的原因最近在YouTube頻道上,有很多人反應維尼的T恤已經鬆掉了、也該換了! 其實單就這個理由還無法讓我們想要購買新的T恤,因為購物是一件非常累人的事情,如果誘因不夠很難讓極簡人購物,哈哈! 這次真正讓我想要更換新的T恤的原因是,我不想再穿內衣了! 這次跟大家分享5個我們不想再穿內衣的原因,以及3種我們
Thumbnail
avatar
布蘭達&維尼
2020-08-28
挑戰你能吃幾盤!平價迴轉壽司大評比台灣人對於日本食物接受度極高,迴轉壽司的價位通常也不算太高,種類多元可以選擇。炎炎夏日又想大快朵頤,就決定是壽司吧!那麼多數的網友們對連鎖迴轉壽司店的討論度又是如何呢?我們挑選出了幾個在台灣較廣為人知的迴轉壽司店:爭鮮、藏壽司、壽司郎、HAMA壽司等,究竟哪一間讓網友都怒吃一波呢?
Thumbnail
avatar
Wisdom字慧輿情
2020-05-29
挑戰Deja vu (似歷其境)的奧秘Deja Vu如果不是腦部系統故障,那會是時空穿梭遊歷嗎? 你有試過明明是「第一次」來到某一個地方、聽某一句話、見某一個人,卻感覺曾親歷其境「似曾相識」嗎? 也許你不只是「Deja Vu」,而是你的潛意識或超意識早就穿越過這時空了。
Thumbnail
avatar
大地僕人札記
2020-05-03
挑戰更少的金錢支出。不消費的2019-01食不過三餐,眠僅需六尺。 想要持續挑戰用更少的物品和金錢支出,維持或得到更好的生活品質。希望有一天,屬於自己的身外之物能少到能隨時說走就走,沒有後顧之憂。
Thumbnail
avatar
壹肆說
2019-02-09
挑戰只靠比特幣生活,在台灣我可以活幾天?這幾天中國有部紀錄片引起了我的興趣,一個比特幣狂熱者發起一項企劃:挑戰用0.21個比特幣活21天!0.21個比特幣換算下來約莫是新台幣4萬元,聽起來用4萬活個21天好像可以過得還不錯...
Thumbnail
avatar
moneybar
2018-10-19