注意:代码我放在了最后面
在写音乐播放器之前我们先了解一下 audio标签 他属于媒体标签一类。
audio标签
- audio标签可以实现在HTML页面中嵌入音频内容。
- audio标签的属性可以设置是否自动播放、预加载、以及循环播放等功能。
- audio标签提供了播放、暂停、和音量控制来控制音频。
- 注意: IE8 或更早版本的IE浏览器不支持 audio 标签。
-
audio标签提供三种音频格式文件
- MP3:采用 mpeg 音频解码器。
- Ogg:采用 ogg 音频解码器。
- Wav:采用 wav 音频解码器。
-
audio 标签支持一下属性
-
src :指定播放文件的URL,可以通过<source>。
-
controls : 激活各个浏览器提供的默认控制。
-
autoplay :加载音频后自动播放。
-
loop :设置该属性后,会循环播放音频。
-
preload :缓存设置。
-
HTML5 针对媒体元素 新增的属性如下
- play() :播放媒体文件。
- pause():暂停播放。
- load():加载媒体文件。
- canPlayType(type):查看浏览器是否支持这种文件格式的媒体文件。
-
paused:视频处于暂停或者未播放状态时,这个值为 true。
-
ended:视频已经结束播放时,这个值为 true。
-
duration:返回媒体时长,以秒为单位。
-
currentTime:获取或设置媒体播放位置。
音乐播放器只要明白一个公式,写起来就很简单。我们先看一下这个公式。
公式:总时间 除以 当前运动时间 = 比例 用比例 乘以 总路程 在赋值给“ 点 ” 。
第一步,点击播放按钮让音乐播放 可以调用audio标签自带的属性 play()和pause()
// 播放歌曲
= function(){
if(onFout==true){
(); //play() 可以使音乐播放
}else{
(); //pause() 可以使音乐暂停
}
}
第二步,实现歌曲进度条 这就要用到我们上面的公式了。
调用audio标签自带的属性 currentTime(当前运动时间);除以 duration(总时间) 乘以 总路程(就是你设置进度条的宽度);~~()这个方法是取整
//歌曲进度条
function ProgressTime(){
//公式:当前运动时间 除以 总时间 =比例; 然后比例*总路程 在赋值给那小点
let a = /;
= ~~(a*)+"px";
= a*+"px";
//把当前运动时间传给页面
let Odangqian = getTime();
let Dqian = ("Dqian");
= `${Odangqian[1]}:${Odangqian[2]}`
}
第三步,拖拽进度条 使进度条同步
首先要先让进度条能拖拽 使用onmousedown鼠标按下、onmousemove鼠标移动、onmouseup鼠标抬起。
如何获取 “ 点 ” 走了多长距离 用 “ 点 ” 到前视口到的位置 减去 “ 点 ”的父级位置到视口的距离 然后再减去 “ 点 ”的宽度 除以 2 这里的(“ 点 ”的宽度 除以 2 意思是鼠标点击之后,鼠标在点的中间位置)。
这里我们就不能使用上面的公式了,要反向思考 。。。我反向思考九九八十一个小时后 得出公式
公式:当前运动时间 除以 总路程 = 比例 然后 比例 乘以 总时间。
currentTime(当前运动时间);除以 总路程(就是你设置进度条的宽度); 乘以 duration(总时间)
此时要注意offsetparent
//拖动进度条
tuodong();
function tuodong(){
= function(ev){
= function(ev){
//offsetParent 元素自身有fixed定位,父元素不存在定位,则offsetParent的结果为null(firefox中为:body,其他浏览器返回为null)
let x = ;
let a = - / 2;
= a+"px";
= a+"px";
//以当前运动时间 除以 总路程 = 比例 比例*总时间 就求出了当时运动到哪里了
let n = parseInt(getComputedStyle(Dian).marginLeft)/;
= n*;
ProgressTime();
}
= function(ev){
= null;
= null;
}
}
}
第四部, 音量,跟上面的进度条拖拽一模一样 只是内容换一下,音量也可以用刚才上面的公式 但是不完全一样 因为 进度条是左右拖动 ,而音量键是上下拖动,音量键最大值也就是1 最小值是0所以一开始是最大值 ,当他往下拖动就变成了 0.9 、0.6、 0.4 、直到0 就没声音了。
所以当套用之前的公式的时候,我们音量键往上拖动是负数 也就是越往上声音越小,明显逻辑不对,应该是越往上声音越大,所以我们在此处减1,变成负数 ,然后再用比例乘以-1 在变回正数
公式:(当前运动时间 除以 总路程)-1 (此时减1是为了变成负数) = 比例 然后 比例 乘以 -1。(此时乘以负1是为了变成正数)
// 音量拖拽
= function(e){
= function(e){
let y = ;
let a = ;
let n = y-a - /2;
if(n>=7 && n<=101){
=n+"px";
=n+"px";
//让音量同步 以当前运动时间 除以 总路程 减1 是为了变成负数
let coP = parseInt(getComputedStyle(Vodian).marginTop)/-1;
let lopT = coP*-1 //此时乘以 负1 是为了变回正数
= lopT;
(coP)
}else{
n = 7;
}
}
= function(e){
= null;
= null;
}
}
ok 此时已经完成了 两大功能 第一是 拖拽进度条 让进度条同步播放 ,第二是 音量拖动 使音量可以*放大缩小。歌词显示 及歌词滚动 也可以用上面的公式 你们可以自己试试看。
如有书写错误还请及时提出,感谢 !非常感谢 !
文件链接:百度网盘 请输入提取码
提取码:7780
代码我放在下方 需要可以自取,
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>精致版播放器</title>
</head>
<link rel="stylesheet" href="">
<body>
<!-- 中屏显示歌词 -->
<div class="zhong-pig">
<!-- 背景虚化 -->
<div class="XW-wu">
<img src="./img/" alt="">
</div>
<!-- 显示歌词滚动 -->
<div class="G-lyric">
<div class="G-back"><img src="./img/" alt=""></div>
<div class="g-kuangci">
<div class="g-biaoti">
<p>奇迹再现</p>
<p><span>专辑:迪迦</span><span>歌手:光</span></p>
</div>
<div class="ge-xianci">
<ul class="geciO" >
</ul>
</div>
</div>
</div>
</div>
<!-- 底部播放器 -->
<div class="B-body">
<div class="yinkuang">
<div class="yinliang">
<div class="Vo-dian" ></div>
<div class="Vo-beijing" ></div>
<div class="Vo-jindu" ></div>
</div>
</div>
<div style="width: 100vw;background-color: #464545;">
<div class="b-waizhao">
<!-- 播放键 -->
<div class="b-play">
<div><img src="./img/上一首.png" alt=""></div>
<div ><img style="display: block;" src="./img/" alt=""><img style="display: none;" src="./img/暂停 (1).png" alt=""></div>
<div><img src="./img/下一首.png" alt=""></div>
</div>
<!-- 进度条 -->
<div class="b-progress">
<div class="tou-X"><img src="/d/file/img/album/" alt=""></div>
<div class="Prss">
<div class="Prss-biaoti">
<p class="bt-1">迪迦-奇迹再现</p>
<p class="s-time">
<span >00:00</span> / <span >00:00</span>
</p>
</div>
<div>
<div class="p-instll">
<div class="p-background"></div>
<div class="p-jintu"></div>
<div class="p-drop"></div>
</div>
</div>
</div>
</div>
<!-- 音量键 -->
<div class="b-volume">
<div ><img style="display: none;" src="./img/" alt=""><img style="display: block;" src="./img/" alt=""><img style="display: none;" src="./img/" alt=""></div>
<div><img src="./img/循环播放_32.png" alt=""></div>
<div><img src="./img/分享 (1).png" alt=""></div>
<div><img src="./img/下载.png" alt=""></div>
</div>
</div>
</div>
</div>
<audio src="./2929593989.mp3"></audio>
</body>
<script>
let lyric = [
{
'name':'奇迹再现',
'img':'/d/file/img/album/',
'audio_src':'/57ea2c6e6532bc05a7fced38f2243af0/6466d9bd/resource/n2/34/34/2929593989.mp3',
'content':"[00:00.000] 作词 : 张鹏[00:01.000] 作曲 : The Bezz[00:04.06][00:16.03]就像阳光穿过黑夜[00:19.08]黎明悄悄划过天边[00:21.98]谁的身影穿梭轮回间[00:27.96]未来的路就在脚下[00:30.95]不要悲伤不要害怕[00:34.04]充满信心期盼着明天[00:39.22]新的风暴已经出现[00:42.02]怎么能够停滞不前[00:44.76]穿越时空竭尽全力[00:48.15]我会来到你身边[00:51.44]微笑面对危险[00:54.02]梦想成真不会遥远[00:56.71]鼓起勇气坚定向前[01:00.11]奇迹一定会出现[01:16.06]就像阳光穿过黑夜[01:19.10]黎明悄悄划过天边[01:21.99]谁的身影穿梭轮回间[01:28.02]未来的路就在脚下[01:30.97]不要悲伤不要害怕[01:34.00]充满信心期盼着明天[01:39.24]新的风暴已经出现[01:42.13]怎么能够停滞不前[01:44.77]穿越时空竭尽全力[01:48.01]我会来到你身边[01:51.55]微笑面对危险[01:54.09]梦想成真不会遥远[01:56.78]鼓起勇气坚定向前[02:00.12]奇迹一定会出现[02:51.33]新的风暴已经出现[02:54.22]怎么能够停滞不前[02:56.77]穿越时空竭尽全力[03:00.20]我会来到你身边[03:03.65]微笑面对危险[03:06.09]梦想成真不会遥远[03:08.77]鼓起勇气坚定向前[03:12.17]奇迹一定会出现[03:15.30]新的风暴已经出现[03:18.19]怎么能够停滞不前[03:20.77]穿越时空竭尽全力[03:23.96]我会来到你身边[03:27.55]微笑面对危险[03:30.10]梦想成真不会遥远[03:32.79]鼓起勇气坚定向前[03:36.18]奇迹一定会出现"
}
];
let audiO = ("audiO");//获取audio的id
let Bplay = ("Bplay");//播放
let bofang = ("bofang");//播放img
let zanting = ("zanting");//暂停img
let ZongLuCheng = ("ZongLuCheng");//总路程
let beijing = ("beijing");//蓝条背景
let Dian = ("Dian");//点
let zongshijian = ("zongshijian")//页面上的歌曲总时间
let timer ;//歌曲播放定时器
let onFout = true;//播放标记
let Vodian = ("Vo-dian");//音量上的点
let Vobeijing = ("Vo-beijing");//音量上的白色背景
let Vojindu =("Vo-jindu");//音量上的进度条
let Vyinliang =("v-yinliang");//鼠标拂过显示音量
let Tyinliang = ("Tyinliang");//调节音量的父级
let geciO = ("geciO");//获取歌词id
let Gcibiaoji = 0; //滚动歌词需要
//转换成时间,用户输入任意秒数, 函数计算该毫秒数对应的时分秒, 并返回
function getTime(time) {
// 转换为式分秒
let h = parseInt(time / 60 / 60 % 24)
h = h < 10 ? '0' + h : h
let m = parseInt(time / 60 % 60)
m = m < 10 ? '0' + m : m
let s = parseInt(time % 60)
s = s < 10 ? '0' + s : s
// 作为返回值返回
return [h, m, s]
}
// 播放歌曲
= function(){
if(onFout==true){
();
="display: none;"
="display: block;"
timer =setInterval(ProgressTime,1000);//每一秒执行一次
onFout = false;
}else{
();
="display: block;"
="display: none;"
onFout = true;
}
}
//歌曲进度条
function ProgressTime(){
//公式:当前运动时间 除以 总时间 =比例; 然后比例*总路程 在赋值给那小点
let a = /;
= ~~(a*)+"px";
= a*+"px";
//把当前运动时间传给页面
let Odangqian = getTime();
let Dqian = ("Dqian");
= `${Odangqian[1]}:${Odangqian[2]}`
Gcibiaoji = parseInt(getComputedStyle(Dian).marginLeft); //滚动歌词需要
}
//判断是否有时间
let zongtimg = setInterval(function(){
if(!=="NaN:NaN" || !== "00:00" ){
let res = getTime();
= `${res[1]}:${res[2]}`
("出现了");
clearInterval(zongtimg);//清除定时器
}else{
("没出现了");
}
},100);
//拖动进度条
tuodong();
function tuodong(){
= function(ev){
= function(ev){
//offsetParent 元素自身有fixed定位,父元素不存在定位,则offsetParent的结果为null(firefox中为:body,其他浏览器返回为null)
let x = ;
let a = - / 2;
= a+"px";
= a+"px";
// 滑动滚动条歌词跟着走
let dd = a*3;
let Heit = ;
(Heit);
if(dd>=0 && dd<=Heit){
= "-"+dd+'px';
}
//以当前运动时间除以总路程 = 比例 比例*总时间 就求出了当时运动到哪里了
let n = parseInt(getComputedStyle(Dian).marginLeft)/;
= n*;
ProgressTime();
}
= function(ev){
= null;
= null;
}
}
}
// 音量拖拽
= function(e){
= function(e){
let y = ;
let a = ;
let n = y-a - /2;
if(n>=7 && n<=101){
=n+"px";
=n+"px";
//让音量同步 以当前运动时间除以总路程 = 比例 比例*总时间 就求出了当时运动到哪里了
let coP = parseInt(getComputedStyle(Vodian).marginTop)/-1;
let lopT = coP*-1
= lopT;
let Yjing = ("Y-jing");
let Ychang = ("Y-chang");
if(lopT == -0){
= "display:block";
= "display: none"
("静音");
}else{
= "display:none";
= "display: block"
}
}else{
n = 7;
}
}
= function(e){
= null;
= null;
}
}
//渲染歌词
let str = '';
init();
function init(){
let g = lyric[0].('[');
((current)=>{
let h = (']');
let lyrics = h[1];
// 切割字符串 转换成时间
let q = h[0].split('.');
let n = q[0].split(':');
let time = n[0]*60+parseInt(n[1]);
// (time);
if(lyrics){
str += '<li >'+lyrics+'</li>';
}
= str;
})
}
//让歌词同步
geciTB();
function geciTB(){
let index = 0;//计数器
("timeupdate",function(){
// ();
//取整
let cur = parseInt();
// (('s'+cur))
if(('s'+cur)){//这里判断的是不是等于空
('s'+index). ='color: #eeeeee';
('s'+cur). = 'color: #55ff00;font-size: 17px;transition: 0.5s;';
index = cur;
(('s'+cur).offsetTop);
//判断 现在个歌词top里他的父级有多元 只要大于258 就让他父级上升50px;布局很重要
if(('s'+cur).offsetTop>=489){
let topf = Gcibiaoji*3;
= "-"+topf+"px";
(topf);
}
}
},false);
}
//鼠标拂过显示音量键
= function(){
let Ylianse = ("Y-lianse")
= "opacity: 1;";
("显示");
= function(){
= "opacity: 0;"
("隐藏")
}
}
//鼠标移开隐藏音量键
= function(){
= "opacity: 1;"
("显示")
= function(){
= "opacity: 0;"
("隐藏")
}
}
</script>
</html>
CSS
*{
margin: 0px;
padding: 0px;
list-style: none;
text-decoration: none;
}
body{
}
/* 底部播放器-开始 */
.B-body{
width: 100vw;
height: 230px;
/* background-color: #b8b5b5; */
position: fixed;
display: flex;
flex-direction: column;
align-items: center;
bottom: 0;
}
.b-waizhao{
width: 900px;
height: 80px;
margin: 0 auto;
display: flex;
justify-content: space-between;
}
/* 播放键 */
.b-play{
width: 145px;
height: 80px;
display: flex;
justify-content: space-between;
align-items: center;
}
.b-play>div>img{
width: 35px;
height: 35px;
cursor: pointer;
}
/* 进度条 */
.b-progress{
width: 500px;
height: 80px;
/* background-color: #214231; */
display: flex;
align-items: center;
justify-content: space-around;
}
.tou-X{
width: 60px;
height: 60px;
}
.tou-X>img{
width: 100%;
height: 100%;
}
.Prss{
width: 390px;
height: 60px;
}
.Prss-biaoti{
width: 100%;
display: flex;
justify-content: space-between;
margin-top: 5px;
}
.bt-1{
font-size: 13px;
color: #f9f6f6;
}
.s-time{
font-size: 13px;
color: #f9f6f6;
}
.p-instll{
width: 390px;
margin-top: 20px;
cursor: pointer;
position: relative;
}
.p-jintu{
width: 390px;
border: solid 1px #f9f6f6;
position: absolute;
}
.p-drop{
width: 10px;
height: 10px;
background-color: #f9f6f6;
border-radius: 5px;
position: absolute;
margin-top: -4px;
z-index: 2;
}
.p-background{
width: 5px;
border: solid 1px #00aaff;
position: absolute;
z-index: 1;
}
/* 音量键 */
.b-volume{
width: 200px;
height: 80px;
/* background-color: aqua; */
display: flex;
align-items: center;
justify-content: space-around;
position: relative;
}
.b-volume>div{
height: 80px;
display: flex;
align-items: center;
}
.b-volume>div>img{
width: 18px;
height: 18px;
cursor: pointer;
}
.yinkuang{
width: 80px;
height: 150px;
/* background-color: #55ff00; */
margin-left: 550px;
opacity: 0;
transition: 0.2s;
}
.yinliang{
width: 25px;
height: 120px;
border-radius: 10px;
background-color: #444444;
display: flex;
flex-direction: column;
align-items: center;
margin: 0 auto;
}
.Vo-dian{
width: 10px;
height: 10px;
border-radius: 5px;
background-color: #00aaff;
z-index: 2;
cursor: pointer;
margin-top: 10px;
}
.Vo-beijing{
width: 4px;
height: 10px;
background-color: #ffffff;
position: absolute;
z-index: 1;
margin-top: 10px;
}
.Vo-jindu{
width: 4px;
height: 100px;
background-color: #00aaff;
position: absolute;
margin-top: 10px;
}
/* 底部播放器-结束 */
.zhong-pig{
width: 100vw;
height: 100vh;
overflow: hidden;
position: relative;
}
/* 歌词开始 */
.G-lyric{
width: 100vw;
height: 100vh;
background-color: rgb(53, 53, 53,0.5);
position: absolute;
display: flex;
align-items: center;
justify-content: center;
}
.G-back{
width: 260px;
height: 500px;
}
.G-back>img{
width: 260px;
height: 280px;
}
.g-kuangci{
width: 460px;
height: 500px;
/* background-color: #55aaff; */
margin-left: 137px;
}
.g-biaoti>p{
width: 300px;
margin-top: 10px;
display: flex;
font-size: 20px;
font-weight: bold;
color: #fff;
justify-content: space-between;
}
.g-biaoti>p>span{
font-size: 15px;
}
.ge-xianci{
width: 400px;
height: 400px;
/* background-color: blueviolet; */
margin-top: 20px;
overflow: hidden;
}
.geciO{
width: 400px;
/* background-color: #ffaa7f; */
transition: 0.3s;
}
.geciO>li{
color: #eeeeee;
font-size: 16px;
margin-top: 10px;
}
/* 歌词结束 */
/* 背景虚化开始 */
.XW-wu{
width: 1200px;
height: 1200px;
position: absolute;
}
.XW-wu>img{
width: 100vw;
height: 100%;
filter: blur(50px);
}
如有书写错误还请及时提出,感谢 !非常感谢 !