今天接着完成部分音乐播放器的功能
主要完成以下功能:
- 默认播放第一首歌,点击播放键即可播放,点击暂停即可暂停
- 点击列表中任意歌曲即切换播放该歌曲
- 实现切换到上一首或下一首的功能
- 进度条随歌曲前进
这里我们用到ajax异步获取到json文件,所以一定要记得在终端启用http-server来在服务端查看,在本地查看不到。
一、默认设置
接上一篇文章【小河今学 | Html+CSS+JS+JQ】音乐播放器1-布局,接下来就是要用js实现音乐播放器的具体功能。
首先我们先设置默认歌曲,这里我选择第一首歌为默认播放的歌曲,即打开页面后第一首歌是默认被选中的。
// 获取到播放器audio
var player = document.getElementById('player');
// 设置初始值
var index = 0; // 默认初始播放第一首歌
// 默认第一首歌
// 设置被选中样式
$('.content li').eq(index).addClass('click');
// 将json中的歌曲地址赋给audio
player.src = songs[index].url;
这时候打开页面控制台,可以看到原本的audio标签已经赋了一个src,即第一首歌的src
接着我们要让他播放和暂停,就要用到audio的play()方法和pause()方法
// 点击播放按钮,播放音乐
$('.pp').click(function(){
// 如果是播放按钮
if($(this).hasClass('play')){
player.play();
// 播放图标变成暂停图标
$(this).removeClass('play');
$(this).addClass('pause');
}else{
player.pause();
// 暂停图标变成播放图标
$(this).removeClass('pause');
$(this).addClass('play');
// 暂停后清理计时器,不清理的话每一秒钟都在调用计时器里的方法
// 但是同时再次点击时有一秒钟的延迟问题,这里如果是为了页面美观可以不要清理计时器
clearInterval(timer);
}
})
设置完后,再次打开页面,这里点击播放键即可播放音乐,同时播放键图标改变。
二、点击播放任意歌曲
在这里我一开始将播放歌曲的方法直接写在li标签的click方法里,但是后续在上一曲下一曲的切换中又用到了播放歌曲,所以这里我直接封装改方法
// 设置一个播放方法
function play(index){
// 改变audio的src
player.src = songs[index].url;
player.play();
// 点击别的歌曲时,若当前播放键为播放(三角形),则换成暂停(直立等于)
if($('.pp').hasClass('play')){
// console.log(11)
$('.pp').removeClass('play')
$('.pp').addClass('pause');
}
}
这里的index即传进来的li标签的下标。
在点击任意歌曲播放里,需要获取到当前点击的这个歌曲的下标
// 定义一个点击方法,当点击这首歌时播放这首歌
$('.content li').click(function() {
// 获取到当前被点击的li标签的下标
var newIndex = $(this).index();
// console.log(newIndex);
// 被点击的歌曲激活样式,其他的不被激活
$(this).addClass('click').siblings().removeClass('click')
// 调用播放方法
play(newIndex);
})
接着回到页面,点击任意歌曲都能开始播放,点击暂停就可停止。
三、实现切换到上一首或下一首的功能
和上面点击播放的功能类似,只是需要调整获得的数组下标。
首先切换到上一首里,我们需要注意当前选中的若是第一首,则再次点击切换到上一首要回到最后一首歌,因此具体代码如下:
$('.prev').click(function() {
// 获取当前播放的歌下标减一
var newIndex = $('.click').index() - 1;
// 如果获取的是第一首歌的下标0,减一为-1,作条件判断语句
newIndex = newIndex < 0 ? $('.content li').length - 1 : newIndex;
// console.log(newIndex)
// 将click赋给获得的新下标的li标签
$('.content li').eq(newIndex).addClass('click').siblings().removeClass('click');
// 调用play方法
play(newIndex)
})
切换到下一首的道理同上
// 点击下一曲,进到下一曲(如果当前是最后一首,则回到第一首)
$('.next').click(function() {
// 获取当前播放的歌下标加一
var newIndex = $('.click').index() + 1;
// 如果获取的是最后一首歌的下标4,加1为5,作条件判断语句
newIndex = newIndex > 4 ? 0 : newIndex;
// console.log(newIndex)
// 将click赋给获得的新下标的li标签
$('.content li').eq(newIndex).addClass('click').siblings().removeClass('click');
// 调用play方法
play(newIndex)
})
四、实时时间变化
这里我们需要先搞定实时时间和总时间,即
获取到总时间,我们需要用到audio的duration属性。在这里我们可以先console.dir(player),看看它有哪些属性值。
控制台输出后,找到duration,就是我们要的总时长。这里以秒为单位,我们需要将它修改成我们需要的格式,再渲染到前端页面上。由于总时长和实时时间的格式修改代码重复,因此这里我将它封装为一个方法。
// 定义一个修改时间格式的方法
function fixTime(time){
// 这里获得的总时长是秒数,我们要把它转换成时间格式
var m = parseInt(parseInt(time) / 60); // 得到分钟数
var s = parseInt(parseInt(time) - m * 60); // 得到秒数
// console.log(s)
// 拼接格式mm:ss
var at = (m < 10 ? "0" + m : m) + ":" + (s < 10 ? "0" + s : s);
return at;
}
这里使用paresInt()是为了得到整数。拼接完毕后记得return才能拿到我们要的字符串。
接着写总时长,这里我原本直接要获得player.duration,但控制台一直输出为NaN,搜索后发现是因为音源是异步加载,所以我们要等到它加载结束后才能获得它的总时长,因此这里我们要用到loadedmetadata
// 获取当前歌曲的总时长,注意这里加载音频资源是异步的,如果没有加载完成就获取歌曲总时长,则duration返回NaN
player.addEventListener('loadedmetadata',function(){ //加载完成后
at = fixTime(player.duration);
// 把拼接好的时间渲染到前端
$('.alltime').html(at);
})
接着写实时时间,因为要实时渲染到前端,这里我们需要用到一个计时器,每秒钟渲染一次。为了避免全局污染,我们首先要在全局定义一个timer=null。
// 显示实时进度
player.addEventListener('playing',function(){
// 这里需要用到计数器,一秒钟刷一次,这样才能达到实时的效果
timer = setInterval(() => {
ct = fixTime(player.currentTime);
$('.curtime').html(ct);
}, 1000);
})
写完后回到页面,可以看到实时时间开始前进,同时控制台每一秒钟刷新一次<span class="curtime"></span>
,这时候按下暂停键可以看到控制台还在不停刷新,因此我们要回到点击暂停的地方,加上一个clearInterval(timer);
来清除计时器。
// 点击播放按钮,播放音乐
$('.pp').click(function(){
// 如果是播放按钮
if($(this).hasClass('play')){
player.play();
// 播放图标变成暂停图标
$(this).removeClass('play');
$(this).addClass('pause');
}else{
player.pause();
// 暂停图标变成播放图标
$(this).removeClass('pause');
$(this).addClass('play');
// 暂停后清理计时器,不清理的话每一秒钟都在调用计时器里的方法
// 但是同时再次点击时有一秒钟的延迟问题,这里如果是为了页面美观可以不要清理计时器
clearInterval(timer);
}
})
最后当歌曲播放结束后,图标需要变回播放键,同时清除计时器。
// 当一首歌播放完毕后,改变图标,同时清理计时器
player.addEventListener('ended',function () {
$('.pp').removeClass('pause');
$('.pp').addClass('play');
clearInterval(timer);
})
五、进度条前进
在原先的html里,除了progress还有一个p,这个p就是前进的进度条。我们要获取当前时间在总时间的占比,转换成进度条的百分比。
// 进度条跟着前进,首先获得当前进度长度
allTime = parseInt(player.duration);
curTime = parseInt(player.currentTime);
var proWidth = (curTime / allTime) * $('.progress').width();
$('.progress p').width(proWidth)
同时,播放完后我发现进度条没有全部铺满,因此进行一个判断语句。完整代码如下:
// 显示实时进度
player.addEventListener('playing',function(){
// 这里需要用到计数器,一秒钟刷一次,这样才能达到实时的效果
timer = setInterval(() => {
ct = fixTime(player.currentTime);
$('.curtime').html(ct);
// 进度条跟着前进,首先获得当前进度长度
allTime = parseInt(player.duration);
curTime = parseInt(player.currentTime);
var proWidth = (curTime / allTime) * $('.progress').width();
$('.progress p').width(proWidth)
// console.log(proWidth)
// 测试后发现播放完后进度条没有满,因此进行一个判断语句
proWidth = proWidth < $('.progress').width() ? proWidth : $('.progress').width();
}, 1000);
})
最终就能获得这样的效果