iT邦幫忙

2021 iThome 鐵人賽

DAY 3
1
Modern Web

從零開始打造網頁遊戲-造輪子你也辦的到!系列 第 3

Chapter1-DJ最愛的音頻動感圖像(II)只要是認識Canvas的都覺得它很High歐

完成上傳機制後,等著我們的是...

https://ithelp.ithome.com.tw/upload/images/20210910/20135197BGJH9L5ErE.jpg
今天的一開始先花一點點時間,把昨天的事件監聽做完吧!這邊準備好一個基本的介面:

Css就不做教學了XD,大家用自己喜歡的樣式唄

<ul>
    <li><button id="Play">Play</button></li>
    <li><button id="Pause">Pause</button></li>
    <li>
        <select id="Select">
            <option>- 請選擇曲名 -</option>
            <option value="Advertime.mp3">Advertime</option>
        </select>
    </li>
    <li><button id="Upload-beautify">- 上傳音樂 -</button></li>
    <input hidden id="Upload" type="file" value="- 上傳音樂 -" accept="audio/*">
</ul>
<audio id="Music" controls>
    <source src="../music/Lovely Piano Song.mp3">
</audio>

依序為昨天沒做完的Play、Pause、Select加上事件監聽:

let audio = document.querySelector("#Music");
let Play = document.querySelector("#Play");
let Pause = document.querySelector("#Pause");
Play.addEventListener("click", audioControl, false);
Pause.addEventListener("click", audioControl, false);
let audioControl = function(){
    // 取得該元件ID的值
    let ID = this.attributes.id.nodeValue; // 'Play' or 'Pause'
    if(ID == "Play") audio.play();
    else audio.pause();
}
Select.addEventListener("change", function(){
    audio.src = "../music/" + this.value;
}, false);

這邊透過共用audioControl函式,就可以統一控制播放和暫停會用到的代碼,預期未來可能會有共用的程式碼。

是不是很簡單呢?其實會放到今天才講,除了讓昨天專注把最複雜的上傳做完之外,也是因為原本我把頻譜分析也放在Play裡面,剛好今天一起講解,不過後來想想,拆開來對大家來說比較乾淨好懂。

重頭戲來了

接下來在搞音樂前,要先做canvas的基本設定和觀念釐清

觀念真的很重要!相信我,學懂了之後可以避免未來debug到瘋掉,

<canvas id="canvas"></canvas>
let canvas = document.getElementById("canvas");
let context = canvas.getContext("2d");
let x = 5, y = 5; // 待會範例使用

這個在習慣上會被簡稱context的物件,有著許多用來畫圖的方法,在現實中,畫圖一般都會先用鉛筆或不顯眼的顏色做底稿,才會真正開始描邊跟著色,Canvas的操作邏輯也很相似且直覺,以「畫黑色的正方形」的三個階段做舉例:

  1. 路徑階段
context.beginPath();  // 準備好新的畫筆
context.moveTo(x, y); // 畫筆不碰到畫布的前提下,移動到(x, y)處放下畫筆
context.lineTo(x + 10, y); // 將畫筆從現在/最後位置(x, y)移動到(x + 10, y),在畫布上留下一段路徑
context.lineTo(x + 10, y + 10);
context.lineTo(x, y + 10);
context.closePath();  // 從最後的位置(x, y + 10)移動到開始的(x, y),留下路徑

closePath實務上較少使用,也容易被混淆,其實不一定要使用,並且可以用context.lineTo(x, y)來替代

  1. 設定階段
context.fillStyle = 'rgb(0,0,0)';  // 設定填滿顏色為黑色
context.lineWidth = 1;             // 設定線段
context.strokeStyle = 'black';     // 設定邊框顏色為黑色
  1. 上色階段
context.fill();      // 著色(剛剛的正方形填滿)
context.stroke();    // 描邊(剛剛的正方形邊框)

應該不難理解,如果少了任何一個階段,就沒辦法成功的把圖畫出來;如果只畫了草稿(路徑),忘了上色,就不會有圖案;如果沒有設定顏色,就直接上色,就像水彩筆沒有沾顏料一樣。

好消息是,當你了解這一個Canvas繪圖的邏輯之後,就不一定每次都要經過這麼完整的步驟了,像是現實中顏料只需要準備一次就好,就可以一直畫直到用完,在Canvas也一樣,只是顏料永遠不會用完,因此如果要連續繪製相同顏色的圖形,就不用每次都重新設定,值得一提的是,能設定的東西相當豐富,除了上面所列出的,還有線條樣式、文字樣式、陰影,等等五花八門,若有用到再跟大家說明吧!

畫圖這麼麻煩?

可能有人會想,我畫個正方形就要寫這麼多行,好累呀!其實,除了自己手刻函數之外,還真的有很方便的原生API可以用唷!跟上面一模一樣的操作,只需要寫下短短幾行:

context.fillRect(x, y, 10, 10);   // 左上角(x, y)為起點,將一個長10寬10的矩形填滿
context.strokeRect(x, y, 10, 10); // 左上角(x, y)為起點,將一個長10寬10的矩形邊框

這兩個函式,實際上幫你把上述提到的幾個必要步驟都包進來了,加上這邊沿用剛剛已設定過的黑色(填滿跟邊框),就能夠如此的簡化,算是新手一大福音,搭配滑鼠游標事件,就可以很輕鬆地做出像是繪圖軟體中的矩形工具,不過這不是本次的主題XD,就不特別實作了,我們的重點可是音樂呀!

不過聰明的你,是否發現了?上面這兩行代碼看起來簡潔,其實重複做了同一件事兩次!它們各自經歷了路徑階段+上色階段,而兩個矩形都是一模一樣的正方形,因此相比最一開始看起來"複雜"的例子,很有可能花費更多時間,這是一個值得思考的點,若未來需要畫上百上千個矩形,又同時有填滿+邊框的效果(也許不同顏色),那是不是自己寫一個function或用長一點的代碼,更能夠優化效能呢?這就留給大家思考了。

今天剩下的時間也不多,只好明天繼續了!

後記

DJ最愛的音頻動感圖像

話說...我其實給大家準備demo拉XD,但沒想到寫文章這麼花時間呀~~~沒辦法衝到我想要的進度,大家就先體驗一下音頻圖像化的魅力吧!(ps: 可以上傳自己喜歡的音樂唷)

今日練習

請想想看,如果給了你長度為1024一個陣列,每筆資料則是介於0~255之間,要怎麼用今天教的方法,畫出像Demo那樣的直方圖呢?你覺得可以設置和調整的參數有什麼?歡迎大家留言回答


上一篇
Chapter1-DJ最愛的音頻動感圖像(I)基本流程圖 & 操作DOM介面
下一篇
Chapter1-DJ最愛的音頻動感圖像(III)媽媽叫你不要玩音樂,現在知道當DJ很難了吧
系列文
從零開始打造網頁遊戲-造輪子你也辦的到!31

尚未有邦友留言

立即登入留言