JavaScript + Audio API自制简易音乐播放器(详细完整版)
**
音乐播放器的功能清单如下:
**
1.点击暂停按钮,歌曲暂停
2.点击播放按钮,歌曲播放
3.单曲循环与取消单曲循环
4.当播放到列表最后一首歌曲时,点击下一首自动切换到列表中的第一首
5.当播放到列表第一首歌曲时,点击上一首自动切换到列表中的最后一首
6.一键静音与非静音
7.增大减小音量(既可通过鼠标点击操作,也可通过键盘上下键操作)
8.下一首、上一首
9.快进、快退
10.进度条随这音乐播放实时变化
11.歌曲已播放时长实时变化
12.点击进度条区域可以进行快进快退,已播放时长和进度条的进度也同步更新
13.鼠标点击拖动进度条的红点(小方块)可以进行快进快退,已播放时长和进度条的进度也同步更新
首先来看一下效果图:
这是我的目录结构:
JavaScript部分的难点:
1.多媒体事件的运用
2.获取Dom对象
3.设置flag记录true false的小技巧,来达到在同一个元素上点击,出现不同的效果
4.鼠标事件的灵活运用
5.通过Dom对象动态修改样式
6.设置定时器
7.键盘事件
CSS部分难点:
1.渐变色
2.pointer-events: none;可以让某个元素实现类似于海市蜃楼的效果,具体理解为,你可以看的到某个元素,但是你无法摸的着。
3.相对定位
4.绝对定位
5.flex弹性盒子布局
6.精灵图background属性的灵活运用
7.属性zoom: 0.2; 强制缩放
zoom牵一发动全身,zoom:0.5后 ,width和height都会变成原来的一半 ;
而transform: scale(0.5) div height200px,width:200px, scale(0.5)即为缩小为原来的一半时,他的height和width还是200px, 只不过里面的元素缩小了而已。
page.js部分的代码如下:
var arrys = ["./songs/晴天 周杰伦.mp3","./songs/时间停了 鹿晗.mp3","./songs/光辉岁月 Beyon.mp3","./songs/栀子花开 何炅.mp3","./songs/纸短情长 烟把儿乐队.mp3"
];var audio = document.createElement("audio");
audio.src = arrys[0];
// 多媒体(Media)事件:
// canplaythrough 事件在视频/音频(audio/video)可以正常播放且无需停顿和缓冲时触发。
// durationchange 事件在视频/音频(audio/video)的时长发生变化时触发。
// pause 事件在视频/音频(audio/video)暂停时触发。
// play 事件在视频/音频(audio/video)开始播放时触发。
// volumechange 事件在音量发生改变时触发。
// ended 事件在视频/音频(audio/video)播放结束时触发。
// error 事件在视频/音频(audio/video)数据加载期间发生错误时触发。
// canplay 事件在用户可以开始播放视频/音频(audio/video)时触发。
// timeupdate 事件在当前的播放位置发送改变时触发。audio.addEventListener("canplaythrough", function() {console.log('music ready');
}, false);
audio.addEventListener("timeupdate", showTime, true);function showTime() {duration = formatTime(audio.duration);currenttime = formatTime(audio.currentTime);document.getElementById('totaltime').innerHTML = duration.M + ':' + duration.S;document.getElementById('currenttime').innerHTML = currenttime.M + ':' + currenttime.S;//获取当前播放的百分比 当前进度/总进度var percent = audio.currentTime / audio.duration//拼接进度条的widthvar swidth = (percent * 255) + "px";//设置进度条的播放进度document.getElementById("bar").style.width = swidth;document.getElementById("dot").style.left = swidth;
}
// 播放
function aPlay() {audio.play();
}
// 暂停
function aPause() {audio.pause();
}
// 快进
function go() {audio.currentTime += 10;audio.play();runToPause();
}
// 快退
function back() {audio.currentTime -= 10;audio.play();runToPause();
}// 将播放图标切换为暂停图标
function runToPause(){runDom[0].style.display = 'none';pauseDom[0].style.display = 'inline-block';
}
// 将暂停图标切换为播放图标
function PauseToRun(){runDom[0].style.display = 'inline-block';pauseDom[0].style.display = 'none';
}quietDom = document.getElementsByClassName('quiet');
noquietDom = document.getElementsByClassName('noquiet');
// 将静音图标切换为非静音图标
function quietToNo(){quietDom[0].style.display = 'none';noquietDom[0].style.display = 'inline-block';
}// 将非静音图标切换为静音图标
function noToquiet(){quietDom[0].style.display = 'inline-block';noquietDom[0].style.display = 'none';
}// 增大音量
function add() {audio.volume != 1 ? audio.volume += 0.1 : 1;console.log(audio.volume);if (audio.volume >= 0.001) {quietToNo();console.log('noquiet')}
}// 减小音量
function subtract() {audio.volume != 0 ? audio.volume -= 0.1 : 0;console.log(audio.volume);if (audio.volume <= 0.001) {noToquiet();console.log('quiet')}
}// 静音
var qFlag = 0; // 是否静音
function isquiet() {qFlag = !qFlag;// if(qFlag){// audio.volume = 0; // console.log(audio.volume);// }else{// audio.volume = 0.5;// } audio.volume = !audio.volume;}
// 静音图标切换为非静音图标
function quiet() {quietToNo();isquiet();
}// 非静音图标切换为静音图标
function noquiet() {noToquiet();isquiet();
}// 时间格式转换
function formatTime(seconds) {var h = 0,i = 0,s = Math.floor(seconds);h = Math.floor(s / 3600);i = Math.floor((s % 3600) / 60);s = s % 3600 % 60;return {H: h = h < 10 ? "0" + h : h,M: i = i < 10 ? "0" + i : i,S: s = s < 10 ? "0" + s : s};
};//设置播放源
var currMp3 = arrys[0];
//上一曲,并实现循环播放
function prev() {tmpMp3 = "";arrys.forEach(function(item, index) {// 通过循环找到与当前播放的音乐相同的那一个item,目的是为了拿到当前的indexif (item == currMp3) {if (index == 0) {tmpMp3 = arrys[arrys.length - 1];} else {//下一个tmpMp3 = arrys[index - 1];}console.log(tmpMp3)audio.src = tmpMp3;setTimeout(function() {audio.play()currMp3 = tmpMp3;}, 500)return;}})run(); // 把播放图标切换成暂停图标并播放音乐
}// 下一曲,并实现循环播放
function next() {tmpMp3 = "";arrys.forEach(function(item, index) {if (item == currMp3) { // 当找到当前播放的歌曲时,进入if代码执行块if ((index + 1) == arrys.length) {//说明是最后一个tmpMp3 = arrys[0];} else {//下一个tmpMp3 = arrys[index + 1];}console.log(tmpMp3)audio.src = tmpMp3;setTimeout(function() { // 设置定时器,在点击下一曲按钮时生效,audio.play(); // play() 方法开始播放当前的音频或视频。currMp3 = tmpMp3; // 将当前播放音乐的路径更新到currMp3中保存}, 500)return;}})run(); // 把播放图标切换成暂停图标,并播放音乐
}var flag = 0; // 是否点击了单曲循环按钮,点了为1,没点为0
// 当前歌曲播放完后,自动切换下一首
audio.addEventListener('ended', function() {if (flag) {arrys.forEach(function(item, index) {if (item == currMp3) {tmpMp3 = arrys[index]}setTimeout(function() {audio.play()currMp3 = tmpMp3;}, 500)})} else {next();}
}, false);// 实现单曲循环
function circle() {console.log('单曲循环')flag = !flag; // 是否点击了单曲循环按钮,点了为1,没点为0console.log(flag);
}document.onkeyup = function(event) { //键盘事件var vol = 0.1; //1代表100%音量,每次增减0.1var time = 10; //单位秒,每次增减10秒// console.log("keyCode:" + event.keyCode);console.log("volume" + audio.volume);var e = event || window.event || arguments.callee.caller.arguments[0];// 键盘事件:
// 属性 描述 DOM
// keydown 某个键盘按键被按下。
// keypress 某个键盘按键被按下并松开。
// keyup 某个键盘按键被松开。//实现键盘按上下键控制音乐的音量,按左右键控制音乐的快进快退if (e && e.keyCode === 38) {// 按 向上键audio.volume !== 1 ? audio.volume += vol : 1;if (audio.volume >= 0.001) {quietToNo();console.log('noquiet')}return false;} else if (e && e.keyCode === 40) {// 按 向下键audio.volume !== 0 ? audio.volume -= vol : 1;if (audio.volume <= 0.001) {noToquiet();console.log('quiet')}return false;} else if (e && e.keyCode === 37) {// 按 向左键audio.currentTime !== 0 ? audio.currentTime -= time : 1;return false;} else if (e && e.keyCode === 39) {// 按 向右键audio.volume !== audio.duration ? audio.currentTime += time : 1;return false;} else if (e && e.keyCode === 32) {// 按空格键 判断当前是否暂停audio.paused === true ? audio.play() : audio.pause();if (audio.paused === false) {runToPause();} else {pauseToRun();}return false;}};
runDom = document.getElementsByClassName('run');
pauseDom = document.getElementsByClassName('pause');
// 点击播放按钮切换为暂停按钮
function run() {runToPause();aPlay();
}
// 暂停按钮切换为播放按钮
function pause() {PauseToRun();aPause();
}// 鼠标事件:
// onclick 当用户点击某个对象时调用的事件句柄。
// ondblclick 当用户双击某个对象时调用的事件句柄。
// onmousedown 鼠标左键被按下。
// onmouseenter 当鼠标指针移动到元素上时触发。
// onmouseleave 当鼠标指针移出元素时触发
// onmousemove 鼠标被移动。
// onmouseover 鼠标移到某元素之上。
// onmouseout 鼠标从某元素移开。
// onmouseup 鼠标左键被松开。window.onload = function() {var scrollDom = document.getElementById('scroll');var barDom = document.getElementById('bar');var dotDom = document.getElementById('dot');// 鼠标左键被按下。scrollDom.onmousedown = function(event) {// console.log("111" + event.offsetX);// 鼠标被移动scrollDom.onmousemove = function(event) {// console.log(event.offsetX);// console.log("offsetLeft" + event.offsetLeft)barDom.style.width = event.offsetX + "px";dotDom.style.left = (event.offsetX) + "px";pause();}}// 鼠标移到某元素上scrollDom.onmouseover = function() {scrollDom.onmousemove = null;}// 鼠标左键被松开scrollDom.onmouseup = function(e) {scrollDom.onmousemove = null; //鼠标左键松开后不做任何操作currentX = e.offsetX;var oWidth = scrollDom.offsetWidth; // 元素整体宽度 :边框+内容区var cWidth = scrollDom.clientWidth; // 元素内容区宽度 :内容区var percents = (currentX / cWidth).toFixed(2);// console.log(percents + '%!')audio.currentTime = audio.duration * percents;run();}scrollDom.onclick = function(e) {console.log(e.offsetX);currentX = e.offsetX;barDom.style.width = e.offsetX + "px";dotDom.style.left = (e.offsetX) + "px";var oWidth = scrollDom.offsetWidth; // 元素整体宽度 :边框+内容区var cWidth = scrollDom.clientWidth; // 元素内容区宽度 :内容区var percents = (currentX / cWidth).toFixed(2);// console.log(percents + '%')// console.log(audio.duration * percents)// console.log(formatTime(audio.duration * percents))audio.currentTime = audio.duration * percents;}}
index.html部分的代码如下:
<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title>简易音乐播放器</title><link rel="stylesheet" href="./css/style.css"><script src="./js/page.js"></script></head><body><div id="all"><div class="speed"><div class="back" οnclick="back()" id="back" title="快退"></div><div class="go" οnclick="go()" id="go" title="快进"></div></div><div class="players"><div class="container clearfix"><div class="left clearfix"><div class="prev" οnclick="prev()" title="上一首"></div><div class="run" οnclick="run();" title="播放"></div><div class="pause" οnclick="pause();" title="暂停"></div><div class="next" οnclick="next()" title="下一首"></div></div><div class="middle"><div class="scroll" id="scroll"><div class="bar" id="bar"><div class="dot" id="dot"></div></div></div><div class="music_detail"><div id="currenttime">00:00</div><div class="line"> / </div><div id="totaltime">00:00</div></div></div><div class="right"><div class="circle" οnclick="circle()" title="单曲循环"></div><div class="quiet" οnclick="quiet()" id="quiet" title="取消静音"></div><div class="noquiet" οnclick="noquiet()" id="noquiet" title="静音"></div><div class="verticle"><div class="add" οnclick="add()" id="add" title="增加音量"></div><div class="subtract" οnclick="subtract()" id="subtract" title="减小音量"></div></div></div></div></div></div></body>
</html>
style.css部分的代码如下:
#progressBar {width: 80%;height: 32%;background: #e9e9e9;position: relative;
}#playProgressBar {position: absolute;top: 0;left: 0;background: #20bfd8;height: 100%;width: 100%;
}#ptxt {width: 100%;height: 30px;text-align: center;font-size: 16px;line-height: 30px;z-index: 10;position: absolute;
}* {margin: 0;padding: 0;
}body {margin: 0 auto;
}
.speed{display: flex;align-items: center;justify-content: center;
}
#all{margin: 200px;
}.clearfix:after {content: '';display: block;visibility: none;clear: both;
}/* 播放图标 */
.run {display: inline-block;width: 220px;height: 217px;background-image: url(../img/icons.jpg);background-position: -267px -20px;zoom: 0.3;}/* float: left; */
/* zoom牵一发动全身,zoom:0.5后 ,width和height都会变成原来的一半 */
/*而transform: scale(0.5) div height200px,width:200px, scale(0.5)即为缩小为原来的一半时,他的height和width还是200px, 只不过里面的元素缩小了而已。 *//* 暂停图标 */
.pause {display: none;width: 220px;height: 217px;background-image: url(../img/icons.jpg);background-position: -517px -517px;zoom: 0.3;
}/* zoom牵一发动全身,zoom:0.5后 ,width和height都会变成原来的一半 */
/*而transform: scale(0.5) div height200px,width:200px, scale(0.5)即为缩小为原来的一半时,他的height和width还是200px, 只不过里面的元素缩小了而已。 *//* 下一首图标 */
.next {display: inline-block;width: 220px;height: 217px;background-image: url(../img/icons.jpg);background-position: -765px -266px;zoom: 0.2;
}/* 上一首图标 */
.prev {display: inline-block;width: 220px;height: 217px;background-image: url(../img/icons.jpg);background-position: -14px -271px;zoom: 0.2;
}/* 单曲循环图标 */
.circle {display: inline-block;width: 220px;height: 217px;background-image: url(../img/icons.jpg);background-position: -514px -23px;zoom: 0.2;
}/* 增大音量图标 */
.add {display: inline-block;width: 90px;height: 90px;background-image: url(../img/voice.jpg);background-position: -163px -125px;zoom: 0.25;
}/* 减小音量图标 */
.subtract {display: inline-block;width: 90px;height: 90px;background-image: url(../img/voice.jpg);background-position: -37px -125px;zoom: 0.25;
}/* 静音图标 */
.quiet {display: none;width: 220px;height: 217px;background-image: url(../img/icons.jpg);background-position: -517px -762px;zoom: 0.2;
}/* 非静音图标 */
.noquiet {display: inline-block;width: 220px;height: 217px;background-image: url(../img/icons.jpg);background-position: -20px -762px;zoom: 0.2;
}/* 快进图标 */
.go {display: inline-block;width: 220px;height: 217px;background-image: url(../img/icons.jpg);background-position: -515px -268px;zoom: 0.2;
}/* 快退图标 */
.back {display: inline-block;width: 220px;height: 217px;background-image: url(../img/icons.jpg);background-position: -268px -268px;zoom: 0.2;
}.players {box-sizing: border-box;width: 770px;height: 110px;background-image: linear-gradient(#436881, #ea9e0d);padding: 13px 0;border-radius: 25px 10px 25px 10px;/* 左上角,右上角,右下角,左下角 */margin: 0 auto;}.container {width: 96%;height: 97%;background-color: white;margin: 0 auto;display: flex;justify-content: space-around;
}.left {width: 23%;height: 100%;display: flex;justify-content: space-around;align-items: center;
}.middle {width: 53%;height: 100%;align-items: center;display: flex;padding: 20px;box-sizing: border-box;
}.right {width: 16%;height: 100%;display: flex;justify-content: space-around;align-items: center;
}.verticle {width: 15%;align-items: center;height: 69%;
}.music_detail {display: flex;}#currenttime {font-size: 13px;line-height: 35px;margin-left: 20px;
}#totaltime {font-size: 13px;line-height: 35px;
}.line {font-size: 13px;line-height: 35px;
}
.scroll{width: 500px;height: 13px;background-color: gray;
}
.bar{width: 0px;height: 13px;background-color: aquamarine;position: relative;pointer-events: none;/* pointer-events: none; 可以让某个元素实现类似于海市蜃楼的效果,具体理解为,你可以看的到某个元素,但是你无法摸的着。 */cursor: move;
}
.dot{width: 5px;height: 13px;background-color: red;position: absolute;left: 0px;top: 0px;
}