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那样的直方图呢?你觉得可以设置和调整的参数有什麽?欢迎大家留言回答


<<:  「行动」是难下的决定,剩下的只是坚持。

>>:  [Day 10] 每家公司都有资料产品

Day030-与Vue相遇--铁人赛回顾

回顾30天,也让我回忆了今年从金融业被抓去做开发工程师的日子。这30天中,讲述了Vue的基本介绍、性...

[Day09] - 未知网址的弹跳视窗 - is 属性

早上查看文章时 , 发现贴到昨天的资料 本鲁立马作文章修改 , 如造成 邦友 的不便 , 请各位海...

Day 07 - Transduce I

从一个简单的问题开始 假设我们目前有一组长度为一百万的阵列,需要将阵列内的每个数值乘三并且只保留偶数...

Day 03:转吧转吧七彩霓虹灯之 p10k

更新 我把从第一天到现在每天的 Home 目录都放上 GitHub 了,README.md 里面有...

铁人赛 Day28 -- AOS.js -- 滚动到元素的位置时,才开始动画

前言 相信很多人都会看到在滑动网站时,滑到一个地方就会出现动画,滑回去再滑回来又会再出现一次, 那这...