在开始前,还没看过序章的朋友们,可以点击进去,教学大纲和主题方向都写在里面罗!
地基一定要打稳,如果基本的还不会的话,建议先去w3school恶补一下!
我们要做的是一个音乐游戏,因此最重要的核心技术便是跟audio这个tag有关的语法,这章节就来把它!
咦?你问我不先设置环境吗,没错XD,由於这是写给入门者的文章,能简单的地方就简单,带大家从实作中学习,因此只需要有大家都有的edge或chrome,和一个文字编辑器(推荐VScode)就可以开始罗。
首先,audio这个元件大家应该不陌生,早在无名小站那个年代,几乎每个网站都会放一个音乐播放器,而且有不少都采用自动播放,笔者就经常不小心开到几个有歌网站,一次播放好几首歌,更甚者有时候还找不到在哪一个分页,堪称网页中的幽灵。
<audio id="Music" controls>
<source src="music/nameOfMyMusic.mp3">
</audio>
既然有现成的控制器能用,我们就先加上controls,这样除错方便
在处理音频的讯号时,我们要用到AudioContext和AnalyserNode,前者能作为我们的音频接口,後者能从中取出即时(当下)的频率资料。
不好懂对吧,就想像我们现在要给自己的笔电接一个新买的喇叭,如果少了AudioContext,就像是笔电上面根本没有插孔,这样喇叭接不上去根本不能用;如果少了AnalyserNode,就像是我喇叭的插头坏了,没办法从中取得音讯。(只是比喻不要太认真xd)
这样应该就不难理解,AudioContext只需要建立一次即可(除非你想在笔电上多挖几个洞XD),AnalyserNode就可以不只一次(可以用好几个不同的喇叭接收音讯),并且为了持续的取得音讯,需要多次用到AnalyserNode的方法,不过我们并没有要用到混音跟创作音乐,所以这次我们只需要各建立一个就好。
这边先咐上今天的流程图,就可以知道这两个元件在哪里作用了:
让我们先把目光放在黄色的菱形,在该流程图中,菱形是一个分歧点,如下图,比方说,我们可以检查使用者按下开始後,是否有先上传音乐,如果有,就走方案A,反之就用方案B。而这张图中则是利用监听事件EventListener来等待使用者进行动作。
那麽,从流程图中,可以知道,用户能做的动作有四种,分别为Play、Pause、Select、Upload,今天就从最基本也最重要的上传开始吧!顺便帮大家暖身一下(可能有些人疫情宅在家都懒了,很久没写程序XD)
<button id="Upload-beautify">- 上传音乐 -</button>
<div class="hidden">
<input id="Upload" type="file" value="- 上传音乐 -" accept="audio/*">
</div>
<script>
document.querySelector("#Upload-beautify").addEventListener(
"click", function(){
document.querySelector("#Upload").click();
}, false);
</script>
因为input不好客制化造型,这边让用户按下button後,再呼叫input
这边也要提醒一下,下面这两种写法都是一样的,如果你复制这段代码到开发者工具的console中,会得到"true",不过我会推荐大家用左边的写法,因为当自己或别人在读code时,从HTML文件看到了这个ID,要到JS中去搜寻的时候,比起"Upload",多了井字号"#Upload"几乎可以立刻找到,在维护上相对会轻松许多。
document.querySelector("#Upload") === document.getElementById("Upload")
console真的很好用,若没指定动作,会直接像console.log一样回传值给你,不过小心别打错字了,如果两边ID都打错,很可能也会得到true唷!因为都找不到有这个ID的元件,会回传null。
接着来实作上传机制,值得注意的是,input标签内的accept="audio/*"
属性只是一个基本的(防笨)机制,实测就会发现,用户在上传档案的时候是"预设"找寻音讯档,然而用户还是能手贱(X)去传其他档案,更关键的问题是,在手机上浏览时,各个装置对这个设定的接受程度也不一,因此防范未然,待会最好还是作一个检查机制。
let Upload = document.querySelector("#Upload");
Upload.addEventListener("change", FileManager, false);
function FileManager(){
console.log(this); // 会印出呼叫该函式的人(初学者这样想就好)
console.log(this.files); // 会印出用户上传的所有档案
console.log(this.files[0]); // 会印出用户上传的第一个档案(或唯一的)
}
这边我们设计一个档案管理函式FileManager,等到用户上传档案後,才开始动作,也因为这里观念蛮重要的,所以通通印出来让大家看一下,这边的this的指的是这个input元件本身,关於this的用法又是一个大坑,只需要知道这边的this会指向这个「ID名为Upload的input元件」为就好,毕竟用onchange呼叫该事件的就是它,这个this不是它还能是谁呢,这个问题很有趣可以思考看看。
那麽档案会传到哪里去呢?当然是在这个物件身上罗,因此透过this.files能浏览用户上传的档案"们",为什麽我强调"们",是因为它是一个阵列,即使用户只上传一个档案,仍会以阵列的方式读取,第一笔资料会存在this.files[0]。
知道这点後,我们可以透过 URL.createObjectURL来取得档案的路径,如果你还是萌新,可能会想为什麽不直接用资料夹路径取得资料,写程序的时候不管是引入外部js、css、甚至图片影片等等,不都是直接提供相对路径吗?其实这是一个保护机制,上传档案的时候,用户的电脑只需要授权网页使用这一个档案,并提供一个假的路径(暂存)blob:接一串英文数字。若直接把整个资料夹路径奉上,岂不是被看光光了,而近年来浏览器更是透过CORS做了严格的限制,想要拿别人的资料,必须得有对方的同意。
function FileManager(){
document.getElementById("Music").src = URL.createObjectURL(this.files[0]);
}
透过这样短短的一行就完成基本的上传音乐了,不过别忘了唷!我们还要来做检查机制,常见的音乐副档名有wav、mp3、m4a,因此我们先建立一个阵列,放进合格的附档名,接着透过this.files[0].name取得完整的档名(字串),只要把字串从中间的(.)给切开就能够取得我们要的副档名了,因此步骤很简单:
function FileManager(){
// 可接受的附档名Exts
let validExts = new Array(".wav", ".mp3", ".m4a");
let index = this.files[0].name.lastIndexOf('.')
let fileExt = this.files[0].name.substring(index);
if (validExts.indexOf(fileExt) < 0) {
console.warn("档案类型错误,可接受的副档名有: " + validExts.toString());
return; //可写可不写,取决於後面还有没有程序码要执行
}
let audio = document.getElementById("Music");
audio.src = URL.createObjectURL(this.files[0]);
}
接下来帮大家回忆一下,当我们仔细拆解一下监听事件的逻辑,就会得到三个关键:
WHO -- 目标是谁
WHEN -- 什麽时候
WHAT -- 要做什麽
就以我们刚刚的上传档案事件为例:
Upload.addEventListener("change", FileManager, false);
// (WHO) (WHEN) (What)
这样就可以搭配一开始给的流程图来理解了,我们要设计一个流程为「当用户上传音乐档案後,先取得该档案的路径,再检查是不是音乐,最後设定音乐的路径」,因此步骤就会很清楚:
透过这个流程,可以清楚的知道分别需要那些语法来完成一连串的动作,这不管是在设计程序逻辑、或是在设计游戏上都相当重要,可以先评估自己在哪个阶段会遇到挑战,虽然有些人天生逻辑很好,可以在脑内想得很清楚,不过在团队合作中,沟通需要一个媒介,这也是LogicFlow存在的意义,不管是设计师、还是程序员,都应该懂得画流程图,
不过,对初学者来说肯定是边做边学习,可以先开始撰写程序码,到一个段落後,把逻辑试着画成一张图,讲给别人听,会是不错的练习唷!
其实我也是为了这次铁人赛才画图的XD,选择的是特别单纯的画法(也为了让大家好懂),只有三个图形,分别是流程起点/终点(圆角矩形)、处理流程(矩形)、抉择点(棱形),之前独立开发,都在自己脑内想好即可,不过,这次透过画出来的方式,也让我有了一次重构程序码的机会,对於後续要加上的流程控制跟例外处理,更是能帮上大忙呢!
因为步骤讲得比较仔细,篇幅拉长,感谢大家耐心观看,希望能帮初学者解惑,如果有什麽问题欢迎在下面做询问!
Q. 请问在今天已经完成的上传流程中,在用户的哪个操作、或是哪段程序码可能会出现错误,甚至导致中断呢?请试着在留言区回答吧!(提示:其实用户不是你想的那麽聪明!)
<<: Day5 - 新鲜人提升开发效率的方法(扩充套件篇)
今天要来介绍 CSS 命名 首先先来介绍 驼峰式命名: https://zh.wikipedia.o...
(https://www.manmonthly.com.au/news/graphene-help...
今天的目标: 要怎麽针对特定资料,固定地创造图表?现在用到图表的机会越来越多,很多时候我们会需要创造...
DBABootcamp 没有 SA 权限的使用者,要如何管理 SQL Agent Jobs (作业)...
新增相片後,要发送通知给相簿应用程序,这样才能更新照片清单,这样才能在相簿看到新增加的照片,使用者体...