Theme NexT works best with JavaScript enabled

ShunNien's Blog

不積跬步,無以致千里;不積小流,無以成江海。

0%

JS30紀錄 01-JavaScript Drum Kit

target gif
JavaScript 30 day 課程第一天,主要目的是按下按鍵時,發出聲音並進行該按鍵文字的特效;透過 Js 來綁定事件,熟練基本 JS 操作。

Demo | Github

處理步驟

步驟 1. 認識 .playing CSS Class

  • 使用 transform 進行縮放

步驟 2. 認識 keycode 還有 HTML 架構

  • 聲音檔案 audio 與 按鍵文字 div 使用 data-key 對應
  • 參考 key code

步驟 3. 建立 keydown listener

  • 建立整份網頁文件檔案的鍵盤監聽事件

步驟 4. 建立 function playSound

  • 取得目前觸發的按鍵 keyCode
  • 取得該 keyCode 對應的文字 div DOM
  • 取得該 keyCode 對應的聲音 audio DOM
  • 當按鍵連續按下時,使聲音檔案重新播放 audio.currentTime = 0
  • 播放聲音 audio.play()
  • 加入 keyCode 對應的文字 div DOM 強調樣式 playing

步驟 5. 建立 transitionend listener

步驟 6. 建立 function removeTransition

  • 利用 transitionend 進行樣式凸顯後移除

  • 在觸發事件內的執行順序

值得一提的是, border-color 會變成四個方向的顏色來設定,可以在 removeTransition function 中添加 log 來查看執行順序與觸發次數。

1
2
3
function removeTransition(e) {
console.log(e.propertyName);
}

當按下觸發按鍵後, removeTransition 的 log 記錄訊息與觸發順序如下流程所示。


graph TD
A(transform)-->B(border-right-color)
B-->C(border-bottom-color)
C-->D(border-top-color)
D-->E(border-left-color)
E-->F(box-shadow)
F-->A1(transform)
A1-->B1(border-right-color
B1-->C1(border-bottom-color)
C1-->D1(border-top-color)
D1-->E1(border-left-color)
E1-->F1(box-shadow)

另外值得一提的是, css 中的樣式順序變動,對於觸發順序毫無影響,也就是說就算把 playing 的 css 變動成如下的順序,觸發順序還是 transform 先開始。

1
2
3
4
5
.playing {
box-shadow: 0 0 1rem #ffc600;
transform: scale(1.1);
border-color: #ffc600;
}

學習事項

將比較不熟練,或是較少使用的事項重複記錄一次。

HTML 部分

HTML audio tag

操作音效檔案的 HTML tag,此處透過 js 操作播放與設定音效檔案重頭播放,可以參考 MDNcurrentTime

HTML kbd tag

就是輸入按鍵的顯示文字,詳細說明可以觀看以下的說明。

Definition and Usage

The tag is a phrase tag. It defines keyboard input.
Tip: This tag is not deprecated, but it is possible to achieve richer effect with CSS.

JavaScript 部分

JS 部分列出一些比較便利的東西來說明。

querySelector

這就是 JS 提供的 Selector 選擇器,有用過 JQuery 的就會感覺很熟悉,跟以下的 querySelectorAll 不太相同的地方是, querySelector 有找尋到結果的時候,會回傳一個 DOM object 。

1
var el = document.querySelector(".myclass");

querySelectorAll

Selector 選擇器的變形,搜尋結果會是類陣列(array-like)

1
var matches = document.querySelectorAll("div.note, div.alert");

需要注意的,是 querySelectorAll 得到的結果並不是 Array

1
2
3
var matches = document.querySelectorAll("div");
Array.isArray(matches);
// false

classList

Element.classList 唯讀屬性代表了該元素所擁有之類別屬性(Class Attribute)的即時更新集-DOMTokenList。

使用 classList 屬性是取得元素 Class 的一種便利方式,也可以透過 element.className 來得到以空格分隔之 Class 清單字串。

1
2
3
4
5
6
7
8
9
10
11
12
13
// div is an object reference to a <div> element with class="foo bar"
div.classList.remove("foo");
div.classList.add("anotherclass");

// if visible is set remove it, otherwise add it
div.classList.toggle("visible");

// add/remove visible, depending on test conditional, i less than 10
div.classList.toggle("visible", i < 10 );

alert(div.classList.contains("foo"));

div.classList.add("foo","bar"); //add multiple classes

addEventListener

EventTarget.addEventListener() 方法能將指定的事件監聽器註冊到 EventTarget 實作物件上。EventTarget 可能是 Document 中的 Element 物件、Document 物件本身、Window 物件,或是其它支援事件的物件(如:XMLHttpRequest)。

transitionend

為 DOM CSS transitions 變換完成後觸發,另外還有 transitionstarttransitioncancel

Array.from

Array.from() 會從類陣列(array-like)或是可迭代的物件建立一個新的陣列實例。

在教學的完成範例中,使用了 Array.from 來轉換 querySelectorAll 的結果,在此範例中,沒有使用此轉換也不影響; 所以才使用 Array.from 來轉換

1
2
3
4
5
6
const bar = ["a", "b", "c"];
Array.from(bar);
// ["a", "b", "c"]

Array.from('foo');
// ["f", "o", "o"]

CSS 部分

介紹 CSS3 的新單位 vhvw ,其瀏覽器的支援程度可以參考

vh

代表的是 view height ,也就是螢幕可視範圍高度的百分比

vw

表示的是 view width ,也就是螢幕可是範圍寬度的百分比。

延伸部分

按著按鈕不放,有時候 Class 樣式沒被移除。
sampel gif picture

幾個解決方式,我決定使用 keyup 事件進行移除,不使用 transitionend 改使用在 keyup 觸發 removeClass function

1
2
3
4
5
function removeClass(e) {
const key = document.querySelector(`.key[data-key="${e.keyCode}"]`);
key.classList.remove("playing");
}
window.addEventListener("keyup", removeClass);

參考資料

歡迎關注我的其它發布渠道