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
//拼接进度条的width
var 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,目的是为了拿到当前的index
if (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,没点为0
console.log(flag);
}
document.onkeyup = function(event) { //键盘事件
var vol = 0.1; //1代表100%音量,每次增减0.1
var 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;
}