为了符合工作需求和学习目标,笔者最近正在学习HTML多媒体相关的内容,其中的一个重点便是音乐播放器,但纵观大部分网站提供的源码及教程,都并未出现相对完整的HTML音乐播放器教学,结合我这几天的学习和实践,总结了一套相对简单的HTML音乐播放器集成代码,在此向大家演示并解释该播放器的内容和愿意,希望能帮助到更多愿意学习web编程的人及业内人员。
直到现在,仍然不存在一项旨在网页上播放音频的标准。今天,大多数音频是通过插件(比如现金已经被淘汰的Flash)来播放的。然而,并非所有浏览器都拥有同样的插件。HTML5 规定了一种通过 audio 元素来包含音频的标准方法,该元素能够播放声音文件或者音频流1。其标准写法如下:
<audio>audio>
本文中提及的音乐播放器为单独页面,不含css和js文件,全部功能仅有一个HTML页面实现,没有多余的东西,下文的代码是逐渐完善的代码片,不是一个整体。直接从本文复制并使用笔者的代码的时候请看清楚,不要一股脑地随意复制粘贴。(只当一个CV程序员你就等着喝西北风吧,哦,国企程序员啊,当我没说……)
新建一个html页面并在body内添加audio控件,暂时不要写其自带的属性:
...
<div>
<audio>audio>
div>
...
通常,在谷歌浏览器下,audio控件看起来是这样的:
资源可以随意添加,比如,我们现在就有三首音乐在我的项目根目录 src/audios 下
制作一个单行的表格,这个表格作为播放列表应该是可以滚动显示的,因此要把表格装载到一个高度固定的div中。首先是添加一个表格:
<h2>播放列表h2>
<div class="musicList">
<table id="myTable">table>
div>
<button>上一首button>
<button>下一首button>
<button id="music_rand">随机播放button>
<input type="checkbox" id="loop_ck">单曲循环button>
为表格添加亿一点点样式,关于个样式代表的含义,这里不做深入解释了,懂点CSS3的人就能看得懂,以及不要计较里面部分很奇怪的样式,不加他们你会后悔的,自己用浏览器调试工具看看效果,这段代买具体可以写在head标签下的style标签中:
<style>
.musicList{width: 300px; height: 10em;border: 1px solid black;table-layout: fixed;}
#myTable{width: 100%;border: 1px solid black;border-collapse: collapse;table-layout: fixed;overflow-y: auto;}
/* 为什么把myTable的高度超出样式设置为自动,后文会讲到 */
td{text-align: left;font-weight: 700; border: 1px solid black;}
th{text-align: left;writing-mode: vertical-rl;transform: rotate(180deg);padding: 0.25em;vertical-align: text-bottom;border: 1px solid black}
style>
不用废话了,就是ajax,有jquery最好(还是那句话,免费的,自己去下一个,以及不要深入学,不然等于49年入*军)。你问我为什么动态加载?这位读者,你是否清醒?播放列表当然会更新啊,你最好是自己会写有关文件上传的东西(不会就搜一个,Servlet上传文件方便的很,甚至都不需要Spring等框架,我总是致力于教大家最简单的东西,所以本文不会涉及文件上传,毕竟那不是必要的东西,而且使用某个播放器的用户也大概率不会自己上传,播放器联网更新后台数据是尝试吧,诸如网易云等诸多平台都是这样做的)。
我用于存储音乐资源列表的是一个JSON文件,内含一个数组,可以通过文件上传实现更新,读者们最好自己也准备一个。
musicName.json:
["歌唱动荡的青春-Lube", "guitar", "lite"]
然后就是添加请求该json的一个ajax,具体如下,写在页面的script标签体内:
<script>
var files = [];
var audio = document.getElementById("audio1");
$.ajax({
url: "./json/musicName.json",
type: "get",
success: function (res) {
files = res;
let ls = res;
// 动态添加音乐资源于audio控件,这条语句↓的作用是设置默认播放的音乐,我选择的是json中的首位
$("#audio1").prop("src", "src/audio/" + res[0] + ".mp3");
for (let i = 0; i < res.length; i++) {
/* 动态生成表格内容,实际上我不推荐这样做,乐曲数量太多会给网页带来加载负担,最好是分次数动态请求,笔者这里是因为只有
三个就稍微写简单一点,笔者会写分次请求,有需要时会教给各位读者。 */
$("#myTable").append('+ i + '" value="' + ls[i]
+ '" οnclick="musicPlaying(`ml' + i + '`)">');
}
}
});
script>
这段代码很多人会有疑惑:第一,为什么会有一个全局变量获取了请求返回的结果res?后面会用到的,还要设计很多内容,为了保证这个数组不会消失,笔者选择以全局变量的形式将其保存,读者们如果会用session和cookie也可以,笔者依旧是那个宗旨,只教最简单的方式,优化代码更方便。第二,res这个返回值可以直接用,为何多写一层内部变量ls承载它?谨慎,毕竟这不是一个公开方法,只是请求成功的内置方法函数,最好用内部变量承载ajax请求成功的返回值,因为可能会涉及到对res的修改,直接修改res会造成一些不必要的效果或错误,尽量修改一个承载变量,而非返回值本身。
各位读者能看到,该请求成功函数将一个input输入框以只读模式加载进了单行表格中,这样做的目的是用户可以点击这个输入框实现音乐的切换,我已经提前内置好一个叫做musicPlaying的函数了,为此,我们需要给这个输入框规定一个类,并修改他们的样式:
<style>
...
.ML{width: 95.5%;padding-left: 0.5em;outline: none;cursor: pointer}
/* 光标指针过于难看,因此用cursor定义了手指类型,也可以附加url使用你们的鼠标图示 */
style>
当ajax请求完成之时,我们就能看到表格的变化了,此时由于乐曲资源已经动态成为了audio标签体内的属性,因此它也变得可以操作了:
这是音乐播放列表的基本功能之一,内置函数musicPlaying开始生效,核心原理是,用户点击了这一栏以后,系统获取到input框内的值,将这个值变成适用于audio控件src属性的完整url,加载这首音乐,然后播放,不仅仅是音乐,视频播放器也可以用我的这个逻辑,B站都上过吧(不是广告不是广告不是广告 )?B站的合集视频也看过的吧?这个播放列表的思想逻辑和那个差不多……
B站UP主“阿卡大文”先生的某个合集视频的播放列表:
上代码:
function musicPlaying(id) {
audio.pause(); // 暂停
$("#audio1").prop("src", ""); // 置空资源,强行停止播放,算是一个保险
/* 点击后用新的value填充 */
let val = $("#" + id).val();
$("#audio1").prop("src", "src/audio/" + val + ".mp3");
audio.play(); // 播放
}
function Playing() {
let name = decodeURI($("#audio1").prop("src").replace("http://localhost:8443/Blog/src/audio/", "").replace(".mp3", ""));
$("input[id*=ml]").each(function () {
let val = $(this).val();
let now = null;
// console.log(name);
if (val.indexOf("正在播放") !== -1) {
now = val.replace("正在播放 —— ", "");
if (now === name) {
$(this).css({"background-color": "black", "color": "white"});
$(this).val(val);
} else {
$(this).css({"background-color": "white", "color": "black"});
if (val.indexOf("正在播放 —— ") !== -1) {
$(this).val(now);
}
}
} else {
if (val === name) {
$(this).css({"background-color": "black", "color": "white"});
$(this).val("正在播放 —— " + val);
} else {
$(this).css({"background-color": "white", "color": "black"});
}
}
});
}
如何调用这个函数呢?如果直接调用,肯定有几率和ajax请求冲突,因此笔者在ajax请求完成后为页面添加了一个监听器,监听我们上文提到的play方法,关于事件监听器的方法不赘述了,读者们可以自行搜索:
$.ajax({
// 略
complete: function () {
audio.addEventListener("play", Playing);
}
});
解释一下为什么会出现now这个中间变量,因为正在播放的乐曲对应播放列表那一栏的value已经更改了,因此要去掉前缀再进行判断,不然这函数的判断条件会使得函数内容间隔执行,这不是我们想要的效果。虽然很简单,但笔者觉得这部分思想已经传达到了,下面是加入了这个部分以后的播放效果:
(该节代码存在参考2)
就如同网易云播放列表的上一首和下一首一样,只是我们采取我们采取比较简单的方式。首先是顺序播放的功能,这个部分其实也不难,之前说过全局变量files的问题,现在就可以用上了,笔者的逻辑是,监听ended结束播放时间,使用一个索引变量index来记录当前乐曲在files中的位置,当播放完成后自动切换下一首(index++),如果此时已经是列表末尾了,则直接将index至于0,代码如下:
function next() {
if (index === files.length - 1) {
index = 0;
} else {
index++;
}
$("#audio1").prop("src", "src/audio/" + files[index] + ".mp3");
audio1.play();
}
在ajax的complete方法中添加ended事件监听器:
$.ajax({
// 略
complete: function () {
audio.addEventListener("play", Playing);
audio.addEventListener("ended", next, false);
}
});
为什么这个方法有一个false?这是事件监听器的第三个参数,主要的目的是区别事件冒泡(false,由内而外,直接抵达触发该事件的控件,然后在上升至整个html document)和事件捕捉(true,由外而内,由html document逐级下降,最终找到触发事件的位置,也就是audios控件)。使用false直接冒泡,时间效率会高很多,虽然可能只是快零点几毫秒,但足够了。
与此同时,这个方法同样可以用来实现手动播放下一首乐曲的功能,因此我们将其与“下一首”按钮绑定,笔者这里直接用的标签体内属性onclick你会用jquery和vue的框架也可以,我把这三个常用框架的表示模式都写出来,以防某些人懒到想直接CV甚至不用改。后面不赘述了,只讲最简单的一种:
html标签体的基本写法:
<button onclick="next();">下一首button>
js内的多种写法(部分例子):
// 记得给button添加id,我这里不赘述,直接写一个id
$("#next").click(()=>{next();});
$("input[id=next]").click(function(){next();});
document.getElementById("next").onclick = function(){next();};
如果使用了Vue,可以这样写:
(html内)
<button @click="next();">下一首button>
或者
<button v-on="{click:next}">下一首button>
(js内)
// 前略
methods:{
next(){
// 内容就是刚才next函数的内容
},
}
以上就是下一首和顺序播放的功能设置与实例化绑定,接下来要进行上一曲的操作,只需要调转next函数的逻辑即可,获取当前的索引位,然后依次递减1,如果已经递减到0了那就回到尾部。直接上代码:
function pre() {
if (index === 0) {
index = files.length - 1;
} else {
index--;
}
$("#audio1").prop("src", "src/audio/" + files[index] + ".mp3");
audio1.play();
}
然后给上一首button控件绑定这个方法:
<button onclick="pre();">上一首button>
所谓单曲循环,其实就是为audio控件动态添加loop属性,甚至不需要写成loop=“loop”,就像input的disabled和readonly一样,浏览器的客制化功能将自动补全属性值,逻辑就是每当点击checkbox时,如果其被勾选了,就添加这个属性开启循环,反之丢掉这个属性退出循环,值得注意的是,循环开启后自动切歌的功能就失效了,因为loop属性的内核其实相当于强行把进度条拖到最开始的位置,而不是重新开始放这首歌。代码如下:
function musicLoop() {
if ($("#loop_ck").prop("checked")) {
$("#audio1").attr("loop", true);
} else {
$("#audio1").removeAttr("loop");
}
}
同时给单曲循环的checkbox绑定上这个功能:
<input type="checkbox" id="loop_ck" onclick="musicLoop();">单曲循环button>
逻辑是,调用生成随机数的函数,生成一个数值在0到数组长度-1(最后一位索引)范围之内的随机数,以此作为索引,获取资源名称并改变audio控件的资源值,这里为了方便(预设了控件id)我直接用jquery绑定了事件函数,代码如下:
// 生成随机数的函数 ↓
function randomNum(minNum, maxNum) {
switch (arguments.length) {
case 1:
return parseInt(Math.random() * minNum + 1, 10);
break;
case 2:
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
break;
default:
return 0;
break;
}
}
$("#music_rand").click(function () {
index = randomNum(0, files.length - 1);
$("#audio1").prop("src", "src/audio/" + files[index] + ".mp3");
audio1.play();
});
先前为列表设置了高度超出自动变化的样式,在这里就能用上了首先要预设播放列表单页的最大显示量,笔者这里给容器的高度限定死了为10em,因此限定表格最多显示六行,该程序的逻辑原理是让表格超出6行以后就滚动显示。如果有需要,读者可自行释放笔者代码的注释部分,这让表格容器可以实时随着表格的高度变化自己的宽度。为了确保该代码不失效,也必须限制其在完成ajax请求后执行,如下:
function makeTableScroll() {
var maxRows = 6;
var table = document.getElementById('myTable');
var wrapper = table.parentNode;
var rowsInTable = table.rows.length;
var height = 0;
if (rowsInTable > maxRows) {
for (var i = 0; i < maxRows; i++) {
height += table.rows[i].clientHeight;
}
// wrapper.style.height = height + "px";
}
}
...
...
...
$.ajax({
// 略
complete: function () {
makeTableScroll() ;
audio.addEventListener("play", Playing);
audio.addEventListener("ended", next, false);
}
});
之后我们就得到可以滚动的播放列表了(不要在意内容,笔者可以添加了重复名称用以测试):
关于其他的功能,诸如样式、变化等,各位可以自行再添加,本文不做其他赘述了。
以上就是笔者能想到的最简单的音乐播放器,重点是如何实现和其他主流音乐播放器,如网易云等相似的播放列表效果,非100%原创,允许转载,欢迎使用。
百度百科词条·HTML5音频 ↩︎
CSDN博客·使用HTML5和JavaScript创建音乐播放列表 ↩︎