
我们接着把剩下的音乐播放器功能也都加上~
上下曲目切换 🦀
切换上一曲 🏄🏼♂️
这个逻辑也很简单,也就是当你点击上一曲按钮的时候,切换到前一首歌~
功能上看上去简单,但是有很多人会在这里犯错~我简单的梳理一下逻辑
功能逻辑
当我们在第一首歌的时候,如果点击上一曲,应该切换到最后一首歌去!
在这里我们之前写的一个initMusic函数就派上用场了~
这个函数我是用来记录当前选中的音乐项目, 并且播放, 默认初始值为0
同时,我们也定义了一个currentIndex来保存索引值~
那么对这个索引值进行减法运算自然可以得到上一曲目的索引
代码如下
//切换上一曲
prevTrack.addEventListener('click',() =>{
currentIndex = currentIndex - 1;
if(currentIndex < 0){
currentIndex = musicList.length - 1;
}
//传递播放曲目索引
initMusic(currentIndex);
//设置一下暂停按钮的显示状态
playPauseBtn.textContent = '❚❚';
//开始播放
audio.play();
});
代码分析
利用当前曲目索引-1, 同时判断是否到顶, 也就是说当前曲目已经是第一首歌了,如果还要点击上一曲目,应该切换到最后一首歌去!
然后把计算好的索引值传递给initMusic函数
注意
这里要特别留意几个问题!~我们在看看之前的这段initMusic函数代码
function initMusic(index) {
audio.src = musicList[index].src;
musicName.textContent = musicList[index].name;
audio.load();
if (!audio.paused) {
audio.play();
}
}
首先我们传递过去的曲目索引值,帮助我们切换到对应的曲目,获取歌曲的一些信息~这个没什么问题
然后audio.load() 会主动触发音频元素的重新加载流程,让浏览器放弃之前的音频缓存,并且重新请求并解析新设置的src对应的音频文件,确保音频能正确关联到最新的歌曲资源~
如果你在这里不调用audio.load()时,浏览器可能不会立即响应src的变更,甚至会保留上一个音频的播放状态、时长等信息,导致新歌曲无法正常播放~比如切换歌曲后还是播放原来的音乐
所以加上audio.load()会重置音频元素的所有状态,包括当前播放时间、缓冲数据等,为新音频的播放做好准备,保证歌曲切换的流畅性和准确性~ 很多人会忽视这一点,导致不少的BUG问题~特别注意一下
同时判断if (!audio.paused) 如果没有暂停播放的情况下,就直接播放audio.play()
这里的意思其实也很简单,作用就是在重新加载新音频后,无缝延续之前的播放状态~ 也就是说原本处于播放中的音频,在我们切换曲目后自动播放新歌曲,不用我们用户再次点击播放~
可能有些人要问我那为什么要在切换上一曲的最后也加上一个audio.play()呢
你其实可以试试看,不加会怎么样,
很显然,你不加,默认情况下,歌曲切换是没问题的,但是就是不会播放
因为initMusic的最后逻辑里audio.play()有前置条件!audio.paused,只有音频原本处于非暂停状态也就是正在播放时才会执行播放~
如果我们不在切换上一曲目的最后加上这个audio.play()那么就没有代码能触发首次播放~懂这个意思吧
这时候,有大聪明要出来说用鼠标把进度条拖拽到中间,播放了, 这总证明音频处于非暂停状态了吧, 按上一曲目 ,同时最后不加audio.play() 也不行啊~
这是因为initMusic函数切换曲目时重新设置了audio.src并执行了audio.load()对吧~
但是这会重置音频状态为暂停paused: true
你可以用属性测试一下就知道了
function initMusic(index) {
audio.src = musicList[index].src;
musicName.textContent = musicList[index].name;
audio.load();
//检测音频/视频是否已暂停
console.log(audio.paused);
if (!audio.paused) {
audio.play();
}
}
如图

看到了吧,之前拖拽播放的非暂停状态会被清除,导致!audio.paused条件不满足,内部audio.play()不执行
知识点
function initMusic(index) {
audio.src = musicList[index].src;
musicName.textContent = musicList[index].name;
audio.load();
//检测音频/视频是否已暂停
console.log(audio.paused);
if (!audio.paused) {
audio.play();
}
}
//切换上一曲
prevTrack.addEventListener('click',() =>{
currentIndex = currentIndex - 1;
if(currentIndex < 0){
currentIndex = musicList.length - 1;
}
initMusic(currentIndex);
playPauseBtn.textContent = '❚❚';
//开始播放
audio.play();
});
另外我们在prevTrack中设置了audio.play()但是在initMusic函数中打印audio.paused还是会返回true
这是因为外部的audio.play()会异步触发音频播放,而initMusic里的console.log(audio.paused)是同步立即执行的,此时音频还未完成播放状态切换,仍显示true,但外部异步的audio.play()最终会成功启动播放。
很多人会在这里出问题就是因为这个原因~所以别当大聪明了,一定要仔细看梳理清楚逻辑才是重点!
如果你之前没有搞懂那个initMusic函数,那么现在你应该清楚了吧~
最后优化一下代码简写
prevTrack.addEventListener('click', () => {
currentIndex = (currentIndex - 1 + musicList.length) % musicList.length;
initMusic(currentIndex);
playPauseBtn.textContent = '❚❚';
audio.play();
});
切换下一曲 🏄🏼
上一曲目切换如果没问题,那下一曲目切换也是同理~
逻辑也很简单,也就是操作当前曲目索引值currentIndex + 1
代码如下
//切换下一曲
nextTrack.addEventListener('click',()=>{
//当前曲目索引+1
currentIndex = currentIndex + 1;
//当超过最后一个曲目的时候,切换到第一首
if(currentIndex>musicList.length-1){
currentIndex = 0;
}
//传递播放曲目索引
initMusic(currentIndex);
//设置一下暂停按钮的显示状态
playPauseBtn.textContent = '❚❚';
//开始播放
audio.play();
});
快退/快进5秒 🦗
这个功能其实本质上是修改audio的currentTime属性
快退5秒 🌙
本质上是在currentTime的基础上减去5秒 , 同时可以结合使用Math.max函数来防止音频的当前播放时间currentTime变成负数,Math.max(0, ...) 能保证即使计算后结果小于 0,最终也只会取0
代码实现
backBtn.addEventListener('click', () => {
audio.currentTime = Math.max(0, audio.currentTime - 5);
});
快进5秒 ⭐️
本质上是在currentTime的基础上加上5秒, 同时可以结合使用Math.min函数来防止音频的当前播放时间currentTime超过音频总时长duration
那么Math.min函数 能保证即使计算后结果大于总时长,最终也只会取音频总时长!
代码实现
nextBtn.addEventListener('click', () => {
audio.currentTime = Math.min(audio.duration, audio.currentTime + 5);
});
双倍快退快进 🐦🔥
这个功能和快进快退的道理是一样的,只是设定的秒数不一样,倍数更大~
普通快进快退为5秒,那么这里双倍自然就是10秒,这根据大家自己的需求自定义即可~
这里我们就直接简单的实现一下
代码实现
// 双倍快退(10秒)
doubleBackBtn.addEventListener('click', () => {
audio.currentTime = Math.max(0, audio.currentTime - 10);
});
// 双倍快进(10秒)
doubleNextBtn.addEventListener('click', () => {
audio.currentTime = Math.min(audio.duration, audio.currentTime + 10);
});
基本功能的JS代码也就写得差不多了, 整体代码如下
// 歌曲列表
var musicList = [
{
src: 'mp3/1.wav', name: 'Once Upon A Time' },
{
src: 'mp3/2.wav', name: 'Instruments of Retribution' },
{
src: 'mp3/3.wav', name: 'Millennia' },
{
src: 'mp3/4.wav', name: 'We Never' }
];
//获取必要的元素
var audio = document.getElementById("audio"); //获取原始音频元素
var musicName = document.getElementById("musicName"); //获取歌曲名称容器
var playPauseBtn = document.getElementById("playPauseBtn"); //暂停和播放按钮
var stopBtn = document.getElementById('stopBtn'); //停止播放按钮
var progressBar = document.getElementById('progressBar'); //获取中层进度条, 控制显示它的状态
var resetBtn = document.getElementById('resetBtn'); //重听按钮
var progressContainer = document.getElementById('progressContainer'); //进度条
var prevTrack = document.getElementById('prevTrack'); //上一曲按钮
var nextTrack = document.getElementById('nextTrack'); //下一曲按钮
var backBtn = document.getElementById('backBtn'); //快退5秒
var nextBtn = document.getElementById('nextBtn'); //快进5秒
var doubleBackBtn = document.getElementById('doubleBackBtn'); //双倍快退
var doubleNextBtn = document.getElementById('doubleNextBtn'); //双倍快进
//定义一个currentIndex变量,用来记录当前选中/激活/播放的项目初始值为0
var currentIndex = 0;
//定义initMusic函数
function initMusic(index) {
audio.src = musicList[index].src;
musicName.textContent = musicList[index].name;
audio.load();
if (!audio.paused) {
audio.play();
}
}
//初始化一下
initMusic(currentIndex);
// 播放/暂停
playPauseBtn.addEventListener('click', () => {
if (audio.paused) {
audio.play();
playPauseBtn.textContent = '❚❚';
} else {
audio.pause();
playPauseBtn.textContent = '▶';
}
});
//停止播放按钮
stopBtn.addEventListener('click', () => {
audio.pause();
audio.currentTime = 0;
playPauseBtn.textContent = '▶';
progressBar.style.width = '0%';
});
// 重听当前歌曲
resetBtn.addEventListener('click', () => {
audio.currentTime = 0;
//这里要考虑到如果用户点击了暂停键,要重听的情况下也要播放
if (audio.paused) {
//开始播放
audio.play();
//设置暂停与播放按钮的内容为❚❚
playPauseBtn.textContent = '❚❚';
}
});
progressContainer.addEventListener('click', (e) => {
var rect = progressContainer.getBoundingClientRect();
//计算当前点击的位置
var clickX = e.clientX - rect.left;
audio.currentTime = (clickX / rect.width) * audio.duration;
//同时播放音乐
audio.play();
});
audio.addEventListener('timeupdate', () => {
if (!audio.duration) return;
var progress = (audio.currentTime / audio.duration) * 100;
progressBar.style.width = progress + '%';
});
//上一曲
prevTrack.addEventListener('click', () => {
currentIndex = currentIndex - 1;
if (currentIndex < 0) {
currentIndex = musicList.length - 1;
}
initMusic(currentIndex);
playPauseBtn.textContent = '❚❚';
audio.play();
});
//下一曲
nextTrack.addEventListener('click', () => {
//当前曲目索引+1
currentIndex = currentIndex + 1;
//当超过最后一个曲目的时候,切换到第一首
if (currentIndex > musicList.length - 1) {
currentIndex = 0;
}
//传递播放曲目索引
initMusic(currentIndex);
//设置一下暂停按钮的显示状态
playPauseBtn.textContent = '❚❚';
//开始播放
audio.play();
});
//快退5秒
backBtn.addEventListener('click', () => {
audio.currentTime = Math.max(0, audio.currentTime - 5);
});
//快进5秒
nextBtn.addEventListener('click', () => {
audio.currentTime = Math.min(audio.duration, audio.currentTime + 5);
});
// 双倍快退(10秒)
doubleBackBtn.addEventListener('click', () => {
audio.currentTime = Math.max(0, audio.currentTime - 10);
});
// 双倍快进(10秒)
doubleNextBtn.addEventListener('click', () => {
audio.currentTime = Math.min(audio.duration, audio.currentTime + 10);
});
// 解决自动播放拦截
document.addEventListener('click', () => audio.play().catch(() => {
}),{
once: true });
最后 🥳
到这里这个简易的web音乐播放器的大致功能我们就做到这里了,其实这个播放器我们还可以继续挖掘它的功能,更具需求不断改进,玩出更多花样~这就要看大家的时间和耐心了
以后有时间,我还会更新一些有趣的小功能~大家一定要多多关注哦!