900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 使用HTML+CSS+JS做一个音乐播放器

使用HTML+CSS+JS做一个音乐播放器

时间:2023-01-24 20:16:41

相关推荐

使用HTML+CSS+JS做一个音乐播放器

一、观前说明:

1.本人为新手,很多地方可能写得不好,欢迎指正。

2.本人仍在学习CSS中,在本篇中若有写得不好的地方,欢迎指正。

3.本人尚未系统性的学习过JS(还没学到),在这里用到的JS全是靠以前学其他语言积累下的基础,因此在很多地方也会写得不够好,欢迎指正。

4.因HTML部分和CSS部分较为简单,本篇文章会更注重于讲JS部分,其中一些说明我会写在代码里面,我认为这比我写在外面更好理解。

5.我是将整个播放器写完了才写的这篇文章,所以你们在看代码的时候,会看到一些提前写了的代码,比如播放暂停还没讲到修改时间间距,但在代码处已经有了。

二、最终效果

2.1、包含功能

1.播放暂停

2.歌词动画,显示当前歌词

3.拖拽歌词,调整歌词进度

4.进度条调整歌词进度

5.音量控制

2.2、图片展示

三、现在开始

1.搭建基础框架

框架图:

按照框架图,搭建框架:

其中歌词是后面由JS创建的,因此只需要有一个div包住就行

<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><link rel="stylesheet" href="css/music.css" /><link rel="icon" href="img/icon.jpg" /><title>音乐播放器</title></head><body><!-- 音乐 --><audio src="audio/韩安旭 - 不在.mp3" preload=""></audio><!-- 背景板 --><div class="background"><!-- 音乐区 --><div class="music-background"><!-- 左侧图片 --><div class="img-back"><img src="img/musicCover2.jpg" alt="韩安旭 - 不在" /></div><!-- 右侧歌词 --><div class="lyrics-back"><!-- 歌曲信息 --><div class="div-title"><h1></h1><p style="margin-right: 100px">专辑:<span></span></p><p>歌手:<span></span></p></div><!-- 歌词信息 --><div class="lyrics-time"><a href="#" class="goto"></a><a href="#" class="goto-time">00:00</a></div><div class="div-lyrics"></div></div></div><!-- 控制区 --><div class="control-back"><!-- 进度条 --><div class="progress-bar"><div class="progress-all"></div><div class="progress-now"></div></div><span class="time">00:00/00:00</span><!-- 控制按钮 --><div class="control"><!-- 上一首、下一首、暂停播放 --><div class="control-btn"><a href="#" class="up"></a><a href="#" class="play-pause" onclick="setPlay()"></a><a href="#" class="down"></a></div><!-- 播放模式、声音 --><div class="control-right"><a href="#" class="mode"></a><a href="#" class="volume" onclick="setMuted()"></a><div class="volume-back"><div class="volume-all"></div><div class="volume-now"></div></div><span class="volume-text">100</span></div></div></div></div><script src="js/music.js"></script></body></html>

2.CSS美化

2.1清除所有边距已经禁止选择

* {margin: 0;padding: 0;user-select: none;}h1 {font-weight: 500;}a {color: #000;text-decoration: none;}

2.2设置背景样式(大盒子)

/* 背景样式,相当于body */.background {width: 100vw;height: 100vh;background-color: #fff;}/* 上半部分样式,即包含封面以及歌词的盒子 */.music-background {width: 1000px;height: calc(100vh - 200px);margin: 0 auto;overflow: hidden;}/* 图片盒子 */.img-back {width: 300px;padding: 0 50px;margin-top: 150px;margin-right: 50px;overflow: hidden;float: left;}/* 歌词盒子 */.lyrics-back {width: 550px;height: 100%;overflow: hidden;float: left;}/* 歌曲信息盒子 */.div-title {width: 100%;height: 100px;margin-top: 150px;}/* 控制区域盒子 */.control-back {width: 100vw;height: 80px;bottom: 0;position: absolute;background-color: rgba(255, 255, 255, 0.8);}

2.3设置图片、字体样式

只是简简单单的调整大小边距等,甚至在歌曲信息中的文字段落标签我都没有设置字体大小,事实上这并不是一种好的习惯

.img-back img {width: 300px;height: 300px;border-radius: 20px;}.div-title h1 {width: 100%;height: 50px;}.div-title p {height: 50px;display: inline-block;color: rgba(0, 0, 0, 0.5);}.div-title span {color: #000;}.div-lyrics p {font-size: 15px;line-height: 34px;}

2.4设置控制按钮、进度条、音量控制样式

这里我的控制按钮用的都是a标签,因此需要将a标签修改为行内块标签inline-block

其中".lyrics-time"是拖拽歌词时显示的调整进度的按钮,因此在默认状态下需要隐藏

.lyrics-time a {display: inline-block;position: absolute;display: none;}.goto {width: 20px;height: 20px;margin: 7px 15px;background: url(../img/play.png) no-repeat;background-position: 50% 50%;background-size: 20px;}.goto-time {width: 50px;height: 34px;margin-left: 550px;font-size: 13px;line-height: 34px;text-align: center;position: absolute;color: #5192fe;}/* 进度条 */.progress-bar {width: calc(100vw - 100px);margin: 5px 0;padding: 5px 0;cursor: pointer;float: left;}/* 当前播放进度 */.progress-now {width: 0;height: 1px;margin-top: -1px;background-color: #5192fe;}/* 总播放进度 */.progress-all {width: 100%;height: 1px;background-color: #dee2e6;}/* 播放进度 文本 */.time {display: inline-block;width: 90px;height: 21px;margin-left: 5px;text-align: center;color: rgba(128, 130, 133, 0.8);cursor: default;overflow: hidden;}/* 控制按钮 */.control {width: 100%;height: 30px;margin-top: 19px;}.control a {margin: 0 5px;display: inline-block;}.control a:hover {opacity: 50%;}.control-btn {width: 130px;height: 30px;margin: 0 auto;}/* 上一首 */.up {width: 30px;height: 30px;background: url(../img/up.png) no-repeat;background-size: 20px;background-position: 50% 50%;}/* 暂停播放 */.play-pause {width: 30px;height: 30px;background: url(../img/play.png) no-repeat;background-size: 30px;background-position: 50% 50%;}/* 下一首 */.down {width: 30px;height: 30px;background: url(../img/down.png) no-repeat;background-size: 20px;background-position: 50% 50%;}/* 右侧控制按钮 */.control-right {/* width: 180px; */height: 30px;margin-left: calc(50vw + 150px);margin-top: -30px;display: flex;align-items: center;}/* 播放模式 */.mode {width: 30px;height: 30px;background: url(../img/sequence.png) no-repeat;background-size: 20px;background-position: left 50%;cursor: pointer;}/* 声音控制 */.volume {width: 30px;height: 30px;background: url(../img/volume.png) no-repeat;background-size: 20px;background-position: left 50%;cursor: pointer;}.volume-back {padding: 5px 0;cursor: pointer;}.volume-all {width: 100px;height: 2px;background-color: #e5e5e5;}.volume-now {width: 100px;height: 2px;margin-top: -2px;max-width: 100px;background-color: #5192fe;}.volume-text {margin-left: 10px;font-size: 14px;}

3.JS部分

3.1首先是声明一些变量、数组等,因为这一部分是要多处使用的,因此在最开头就声明了

var myAduio = document.getElementsByTagName('audio')[0];var divLyrics = document.getElementsByClassName('div-lyrics')[0];var divTitle = document.getElementsByClassName('div-title')[0];var lyricsTime = document.getElementsByClassName('lyrics-time')[0];var lyricsTime_a = lyricsTime.getElementsByTagName('a');var progressTime = document.getElementsByClassName('time')[0];var nowLine = 0;var lyricsMove = false;var playState = false;var lyrics, lyricsStyle, lyricsFirst, rollT;var timeArray1 = new Array();var timeArray2 = new Array();var timeInterval = new Array();

这里面分别是

audio控件,播放音乐用的;

歌词div,到时候要在这里面创建歌词

歌曲信息div,也是在这里面修改歌曲信息的内容

拖拽歌词调整进度的div "lyricsTime"以及里面的按钮 “lyricsTime_a”

进度条右侧的时间"progressTime"

当前播放行"nowLine"

拖拽状态"lyricsMove"

播放状态"playState"

所有歌词的p标签"lyrics",第一行歌词的样式"lyricsStyle",第一行歌词的p标签"lyricsFirst",歌词滚动的计时器"rollT"

以秒数记录每一行歌词所在时间的数组"timeArray1"以及以分钟:秒数的星石记录每一行歌词所在时间的数组"timeArray2",用两种方式记录时间的原因是后面需要以这两种形式交叉使用,也可以只记录一种,在使用到另一种的时候将其算出来,但我感觉直接记录两种会更方便

记录每一行歌词时间间距的数组"timeInterval"

3.2页面加载完毕后就需要执行的方法

window.onload = function () {initialLyrics();lyricsStyle = getComputedStyle(lyricsFirst, null);setLyrics(0);setMouseEvent();setTimeText();};

3.3初始化歌词(创建歌词并存储一些信息)

sp、ar、ti分别为专辑、艺术家、歌名

function initialLyrics() {let sp = divTitle.getElementsByTagName('span')[0];let ar = divTitle.getElementsByTagName('span')[1];let ti = divTitle.getElementsByTagName('h1')[0];let lyricsData, timeString;let lyricsArray = new Array();// 清除数组,先清除数组可以保证每一次存储的信息无误timeArray1.splice(0, timeArray1.length);timeArray2.splice(0, timeArray2.length);lyricsArray.splice(0, lyricsArray.length);// 按相同格式放入歌词更换歌曲即可达到相同效果lyricsData ='[ar]韩安旭\n[ti]不在\n[sp]不在\n[00:00.74]韩安旭 - 不在\n[00:01.76]词:尤雅琪\n[00:02.76]曲:胜屿\n[00:15.62]我累了就紧紧锁住情绪\n[00:18.11]不再放任它堆积\n[00:22.14]我痛了就静静屏住呼吸\n[00:26.02]不给想念留余地\n[00:28.88]只是下雨时会委屈\n[00:32.80]只是想起你会哭泣\n[00:36.77]没关系 真没关系\n[00:44.28]我终于学会一个人弹琴\n[00:47.12]只是弹琴没有你\n[00:49.29]我终于学会一个人做梦\n[00:54.85]只是做梦没有你\n[00:57.73]我依旧像从前粗心\n[01:01.09]时常会忘记星期几\n[01:05.00]却始终忘不掉你看我的眼睛\n[01:11.71]穿过了熙攘的人海\n[01:15.11]想找谁能把你取代\n[01:19.62]复制你曾给过我的\n[01:21.44]那种宠爱\n[01:26.32]掏空了回忆的脑海\n[01:30.75]寂寞却狠狠扑过来\n[01:33.69]措手不及 无法躲开\n[01:41.52]我承认是我太依赖\n[01:44.92]像个不懂事的小孩\n[01:48.35]挥霍掉我们的未来\n[01:51.22]才醒过来\n[01:55.15]我承认后悔了伤害\n[01:59.06]抛开你的好我的坏\n[02:02.14]直到如今学会忍耐 你不在\n[02:26.95]我终于学会一个人弹琴\n[02:29.33]只是弹琴没有你\n[02:33.26]我终于学会一个人做梦\n[02:36.64]只是做梦没有你\n[02:39.53]我依旧像从前粗心\n[02:42.90]时常会忘记星期几\n[02:46.82]却始终忘不掉你看我的眼睛\n[02:53.62]穿过了熙攘的人海\n[02:57.09]想找谁能把你取代\n[03:00.98]复制你曾给过我的\n[03:05.43]那种宠爱\n[03:08.25]掏空了回忆的脑海\n[03:11.67]寂寞却狠狠扑过来\n[03:15.56]措手不及 无法躲开\n[03:22.49]我承认是我太依赖\n[03:26.37]像个不懂事的小孩\n[03:30.38]挥霍掉我们的未来\n[03:33.80]才醒过来\n[03:37.81]我承认后悔了伤害\n[03:41.29]抛开你的好我的坏\n[03:44.73]直到如今学会忍耐 你不在';// 文本.split('分隔符'),用于分割文本lyricsArray = lyricsData.split('\n');// 添加歌曲信息ar.innerText = lyricsArray[0].split(']')[1];ti.innerText = lyricsArray[1].split(']')[1];sp.innerText = lyricsArray[2].split(']')[1];// 添加歌词for (var i = 3; i < lyricsArray.length; i++) {//这里i=3即我们的第一行歌词,将高度设置为100%,在页面创建完毕时添加一个上滚的动画if (i == 3) {divLyrics.innerHTML +='<p style="margin-top: 100%;color:#5192fe;">' +lyricsArray[i].split(']')[1] +'</p>';} else {divLyrics.innerHTML +='<p>' + lyricsArray[i].split(']')[1] + '</p>';}}// 获取后续需要使用的变量lyricsFirst = divLyrics.getElementsByTagName('p')[0];lyrics = divLyrics.getElementsByTagName('p');// 计算每局歌词所在的秒数timeArray1.push(0);for (var i = 0; i < lyrics.length; i++) {timeString = lyricsArray[i + 3].substring(1, 9).split(':');timeArray1.push(parseFloat(timeString[0]) * 60 + parseFloat(timeString[1]));}// 计算时间间隔,将时间从秒数改为分钟+秒数for (var i = 0; i < timeArray1.length - 1; i++) {timeInterval[i] = timeArray1[i + 1] - timeArray1[i];// timeArray2数组主要是显示使用的,在秒数部分,若为个位数,十位数补零会更为美观if (Math.floor(timeArray1[i] % 60) < 10) {timeArray2.push(Math.floor(timeArray1[i] / 60) +':0' +Math.floor(timeArray1[i] % 60));} else {timeArray2.push(Math.floor(timeArray1[i] / 60) +':' +Math.floor(timeArray1[i] % 60));}}}

歌词初始化完后,整个页面就可以正常显示了,接下来先写控制播放暂停的功能

3.3播放暂停

// 设置播放状态function setPlay(state) {var play_pause = document.getElementsByClassName('play-pause')[0];if (state == null) {// 如果歌曲为暂停状态,那么获取到的state则为true,将state设置为true我们就播放state = myAduio.paused;}// 清除计时器,不然会出现多个计时器同时进行clearTimeout(rollT);if (state == true) {myAduio.play();play_pause.style.backgroundImage = 'url(../img/pause.png)';playState = true;// 开始播放的同时,同时开始设置时间进度文本,歌词滚动以及进度条位置setTimeText();lyricsRoll();setProgress();} else {myAduio.pause();play_pause.style.backgroundImage = 'url(../img/play.png)';playState = false;// 暂停后重新修改计时器时间,这里后面会详细讲timeInterval[nowLine] = timeArray1[nowLine + 1] - myAduio.currentTime;}}

3.4设置时间进度文本

// 设置进度文本function setTimeText() {var nowTime = myAduio.currentTime;var allTime = myAduio.duration;// 计算时间,若为个位数,补0if (Math.floor(nowTime % 60) < 10) {nowTime = Math.floor(nowTime / 60) + ':0' + Math.floor(nowTime % 60);} else {nowTime = Math.floor(nowTime / 60) + ':' + Math.floor(nowTime % 60);}if (Math.floor(allTime % 60) < 10) {allTime = Math.floor(allTime / 60) + ':0' + Math.floor(allTime % 60);} else {allTime = Math.floor(allTime / 60) + ':' + Math.floor(allTime % 60);}progressTime.innerText = nowTime + '/' + allTime;// 每0.1秒执行一次if (myAduio.paused == false) {setTimeout(setTimeText, 100);}}

3.5设置进度条位置

// 设置进度条进度function setProgress() {let progress_now = document.getElementsByClassName('progress-now')[0];let progress_bar = document.getElementsByClassName('progress-bar')[0];let progress = Math.floor((myAduio.currentTime / myAduio.duration) * progress_bar.clientWidth);progress_now.style.width = progress + 'px';if (myAduio.paused == false) {setTimeout(setProgress, 100);}}

到这里,除了歌词滚动、调整进度等,一个最简单的音乐播放器就已经完成了

3.6设置歌词位置

// 设置歌词位置function setLyrics(line) {// 将当前歌词高亮,其余歌词都改为黑色for (let i = 0; i < lyrics.length; i++) {lyrics[i].style.color = '#000';}lyrics[line].style.color = '#5192fe';// 设置动画,这里只需要改变第一行歌词的位置即可// 这里是将第一行歌词的marginTop从当前的位置修改为当前进度歌词所在行的对应位置,因为我将p标签的行高设置为了34px,而我要将当前歌词在第五行显示,即前面要有四行空的,可以得到34*4=136px的marginTop,line * (-34) + 136即我需要向上移动多少行// 假设我的当前歌词是在第一行显示,当前歌词在第一行(数组从零开始,所以这里在数组里是0),那么marginTop就是0,即0 * (-34),当前歌词在第二行,那么marginTop就是-34px,即1 * (-34),往后以此类推// 同上,比如当前歌词在第一行,但是我需要将第一行歌词显示在第五行,即前面有四个空的位置,那么我第一行歌词就需要将marginTop修改到第五行的位置,那么第一行歌词的marginTop就是136px,即0 * (-34) +136let lyrics_animation = lyrics[0].animate([{marginTop: lyricsStyle.marginTop,},{marginTop: line * -34 + 136 + 'px',},],{duration: 100,});//设置监视器,在动画完成之后,修改第一行歌词的marginTop,这里不用"fill:forwards"的原因是使用后,拖拽歌词将无法使用lyrics_animation.addEventListener('finish',function () {lyrics[0].style.marginTop = line * -34 + 136 + 'px';},false);}

设置歌词写好了,我们现在就需要考虑如何让歌词滚动起来,即让歌词自动到达当前行的位置

我想到的方法有两种

第一种方法是根据每句歌词之间的时间间隔,每过一个间隔,就触发一次方法,我这里用的就是这种方法;

第二种方法是每过很短的一段时间,就触发一次方法,就跟进度条类似;

我观察过酷狗以及QQ音乐,其中酷狗用的应该就是第一种方法,是过一个间隔触发一次的,而QQ音乐则是用的第二种方法。

我个人认为第二种方法写起来更简单,但是需要的性能更高,但是进度条、以及时间进度文本是必须使用第二种方法的(至少我想不到其他的),那么如果将进度条、时间进度文本、歌词滚动这三个方法放在一个计时器中调用,而不是像我这样分开三个计时器,哪一种是更优的方法就不得而知了,欢迎各位大佬给出自己的看法。

那么我们这里用第一种方法,首先我们先分析一下需要记录的内容(其实前面已经记录了,只是在这里才说)

第一个是要记录歌词,这个不需要多的解释,显示用的

第二个需要记录时间间隔,这个是用来设置计时器的

第三个需要记录每一行歌词所在的时间,分两个记录,一个按秒数记录,用来拖拽歌词时调整进度;另一个按分:秒记录,用于拖拽歌词时显示时间;两个数组可只记录一个,用到另一个的时候算出来就好,我为了方便就记录了两个

前面三个都是要用数组记录,第四个则是非数组,用于记录当前歌词到了哪一行,在歌词滚动中,歌词回弹等地方都会用到。

需要记录的东西我们分析完了,那么接下来在画一个逻辑图来分析计时器要如何设置,即歌词滚动要如何实现

分析完毕,那么继续

3.7歌词滚动

// 歌词滚动// 歌词滚动的方法只需要按照时间间隔设置计时器即可function lyricsRoll() {rollT = setTimeout(function () {if (nowLine < lyrics.length && myAduio.paused == false) {if (lyricsMove == false) {setLyrics(nowLine);}nowLine += 1;lyricsRoll();}}, timeInterval[nowLine] * 1000);}// 以下代码不在这个地方,在设置播放状态处,只是放在这里做解释// 每当我们暂停歌曲时,我们就需要将当前行到下一行即从i到i+1的时间间距,从原来的,修改为剩余的,比如本来跳到下一行需要10s,而我是在第5s的时候暂停的,那么剩余时间就是5s,但我们无法直接知道在这一行歌词我们用了多少时间,所以我们就要用下一行歌词的时间(不是间距,而是在整首歌中这一行歌词在哪一秒)减去当前的播放进度,这样得到的值就是我们需要的剩余间距,所以代码是timeInterval[nowLine] = timeArray1[nowLine + 1] - myAduio.currentTime;

到这里,播放器的歌词滚动部分已经写好了,但现在的播放器只能播放暂停,看到歌词在哪,不能调整进度等,接下来我们就来解决这个问题

3.8歌词拖拽、音量控制、调整进度

function setMouseEvent() {// 歌词拖拽let lyrics_Y, line;// 此处是调整歌词位置,以及计算用户将歌词拖拽到了哪一行divLyrics.onmousedown = function (e) {if (lyricsMove == false) {lyricsTime_a[0].style.display = lyricsTime_a[1].style.display = lyricsTime.style.display ='block';lyricsMove = true;}lyrics_Y = parseInt(lyricsStyle.marginTop);document.onmousemove = function (event) {lyricsFirst.style.marginTop =event.clientY - (e.clientY - lyrics_Y) + 'px';line = Math.floor(-(parseInt(lyricsStyle.marginTop) - 170) / 34);if (line < 0) {line = 0;} else if (line > lyrics.length - 1) {line = lyrics.length - 1;}lyricsTime_a[1].innerText = timeArray2[line];};document.onmouseup = function () {// Y1的作用是判断用户是否仍处于拖拽状态,若一秒后歌词位置仍等于Y1,则判断为非拖拽状态,但这样写有一个bug就是如果只是按住鼠标不进行拖拽,则也会判断为非拖拽状态,暂时想不到解决方法var lyrics_Y1 = parseInt(lyricsStyle.marginTop);setTimeout(function () {if (parseInt(lyricsStyle.marginTop) == lyrics_Y1) {lyricsMove = false;setLyrics(nowLine - 1);lyricsTime_a[0].style.display = lyricsTime_a[1].style.display = lyricsTime.style.display ='none';}}, 1000);// 清除鼠标移动方法的同时也一定要清除鼠标弹起方法,不然每次点击页面都会调用这个方法document.onmousemove = null;document.onmouseup = null;};// 防止选中文字return false;};// 音量控制// 音量控制没什么好说的,需要注意的是volume的音量是从0-1,所以在设置音量时要将其除以100let volume_now = document.getElementsByClassName('volume-now')[0];let volume_back = document.getElementsByClassName('volume-back')[0];let volume_text = document.getElementsByClassName('volume-text')[0];let volume_a = document.getElementsByClassName('volume')[0];volume_back.onmousedown = function (e) {volume_now.style.width = e.offsetX + 'px';myAduio.volume = e.offsetX / 100;volume_text.innerText = volume_now.clientWidth;volume_back.onmousemove = function (ev) {let volume = ev.offsetX;if (volume > 100) {volume = 100;}volume_now.style.width = volume + 'px';myAduio.volume = volume / 100;volume_text.innerText = volume_now.clientWidth;};document.onmouseup = function () {// 如果音量为0,更换静音图片,否则更换非静音图片if (myAduio.volume == 0) {volume_a.style.backgroundImage = 'url(../img/mute.png)';} else {volume_a.style.backgroundImage = 'url(../img/volume.png)';}volume_back.onmousemove = null;document.onmouseup = null;};return false;};// 进度控制// 进度控制跟音量控制一样,只需要注意算法就好// 从之前的设置进度条进度我们可用知道,进度条的宽度=当前播放进度/歌曲总时长*进度条总长// 所以 当前播放进度=进度条宽度/进度条总长*歌曲总时长let progress_now = document.getElementsByClassName('progress-now')[0];let progress_bar = document.getElementsByClassName('progress-bar')[0];progress_bar.onmousedown = function (e) {progress_now.style.width = e.offsetX + 'px';myAduio.pause();myAduio.currentTime =(e.offsetX * myAduio.duration) / progress_bar.clientWidth;setTimeText();progress_bar.onmousemove = function (ev) {let progress = ev.offsetX;if (progress > progress_bar.clientWidth) {progress = progress_bar.clientWidth;}progress_now.style.width = progress + 'px';myAduio.currentTime =(progress * myAduio.duration) / progress_bar.clientWidth;setTimeText();};document.onmouseup = function () {myAduio.play();for (var i = 0; i < timeArray1.length; i++) {if (myAduio.currentTime < timeArray1[i]) {// 获取调整进度后当前处于哪一行歌词,假设我调整后的进度是1s,那么在数组中第一个大于1s的的上一个,就是当前行,所以需要-1// nowLine是i - 1 但是设置歌词位置却是 i - 2 的原因,因为我们的timeArray1是在开头多加了一个元素的,也就是说timeArray1数组比歌词数组多了一个元素,所以要再-1,而在lyricsRoll方法中,我是先设置计时器再将nowLine+1的,所以这里的nowLine要+1,那么总体结果就是,nowLine = i -1 , 设置歌词为 i - 2nowLine = i - 1;setLyrics(i - 2);timeInterval[nowLine] =timeArray1[nowLine + 1] - myAduio.currentTime;setPlay(true);break;}}progress_bar.onmousemove = null;document.onmouseup = null;};return false;};// 拖拽歌词调整进度,需要注意的也只是算出用户拖到了第几行而已,在拖拽歌词事件中(在上面)我们就已经将行数算出来了,这里直接调用就可用// 也说一下算法,因为我们是按照行来设置第一行歌词的marginTop的,那么反推过来就是// 行数=-(第一行歌词的marginTop-136)/34// 同样的,因为timeArray2数组多一个元素,所以显示拖拽歌词时显示的歌曲进度就要+1,我这里设置歌词位置是line - 1的原因是我在算行数的时候,将136改为了170,即我在算行数的时候就已经+1了let goto = document.getElementsByClassName('goto')[0];goto.onmouseup = function () {nowLine = line;myAduio.currentTime = timeArray1[line];setLyrics(line - 1);setPlay(true);lyricsMove = false;lyricsTime_a[0].style.display = lyricsTime_a[1].style.display = lyricsTime.style.display ='none';document.onmouseup = null;};}

3.9设置静音

这个比较简单,就不说了

// 设置静音function setMuted() {let volume_now = document.getElementsByClassName('volume-now')[0];let volume_text = document.getElementsByClassName('volume-text')[0];let volume_a = document.getElementsByClassName('volume')[0];if (myAduio.muted == true) {myAduio.muted = false;volume_a.style.backgroundImage = 'url(../img/volume.png)';volume_now.style.width = myAduio.volume * 100 + 'px';volume_text.innerText = myAduio.volume * 100;} else {myAduio.muted = true;volume_a.style.backgroundImage = 'url(../img/mute.png)';volume_now.style.width = '0';volume_text.innerText = '0';}}

到这里,我们的整个播放器就已经全部完成了

以下是完整代码

HTML

<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><link rel="stylesheet" href="css/music.css" /><link rel="icon" href="img/icon.jpg" /><title>音乐播放器</title></head><body><!-- 音乐 --><audio src="audio/韩安旭 - 不在.mp3" preload=""></audio><!-- 背景板 --><div class="background"><!-- 音乐区 --><div class="music-background"><!-- 左侧图片 --><div class="img-back"><img src="img/musicCover2.jpg" alt="韩安旭 - 不在" /></div><!-- 右侧歌词 --><div class="lyrics-back"><!-- 歌曲信息 --><div class="div-title"><h1></h1><p style="margin-right: 100px">专辑:<span></span></p><p>歌手:<span></span></p></div><!-- 歌词信息 --><div class="lyrics-time"><a href="#" class="goto"></a><a href="#" class="goto-time">00:00</a></div><div class="div-lyrics"></div></div></div><!-- 控制区 --><div class="control-back"><!-- 进度条 --><div class="progress-bar"><div class="progress-all"></div><div class="progress-now"></div></div><span class="time">00:00/00:00</span><!-- 控制按钮 --><div class="control"><!-- 上一首、下一首、暂停播放 --><div class="control-btn"><a href="#" class="up"></a><a href="#" class="play-pause" onclick="setPlay()"></a><a href="#" class="down"></a></div><!-- 播放模式、声音 --><div class="control-right"><a href="#" class="mode"></a><a href="#" class="volume" onclick="setMuted()"></a><div class="volume-back"><div class="volume-all"></div><div class="volume-now"></div></div><span class="volume-text">100</span></div></div></div></div><script src="js/music.js"></script></body></html>

CSS

* {margin: 0;padding: 0;user-select: none;}h1 {font-weight: 500;}a {color: #000;text-decoration: none;}/* 背景板 */.background {width: 100vw;height: 100vh;background-color: #fff;}/* 歌词封面背景 */.music-background {width: 1000px;height: calc(100vh - 200px);margin: 0 auto;overflow: hidden;}/* 封面背景 */.img-back {width: 300px;padding: 0 50px;margin-top: 150px;margin-right: 50px;overflow: hidden;float: left;}.img-back img {width: 300px;height: 300px;border-radius: 20px;}/* 歌词背景 */.lyrics-back {width: 550px;height: 100%;overflow: hidden;float: left;}/* 歌曲信息 */.div-title {width: 100%;height: 100px;margin-top: 150px;}.div-title h1 {width: 100%;height: 50px;}.div-title p {height: 50px;display: inline-block;color: rgba(0, 0, 0, 0.5);}.div-title span {color: #000;}.div-lyrics {height: calc(100vh - 450px);overflow: hidden;position: relative;}.div-lyrics p {font-size: 15px;line-height: 34px;}/* 歌词进度 */.lyrics-time {width: 650px;height: 34px;margin-top: 136px;margin-left: -50px;position: absolute;display: none;}.lyrics-time a {display: inline-block;position: absolute;display: none;}.goto {width: 20px;height: 20px;margin: 7px 15px;background: url(../img/play.png) no-repeat;background-position: 50% 50%;background-size: 20px;}.goto-time {width: 50px;height: 34px;margin-left: 550px;font-size: 13px;line-height: 34px;text-align: center;position: absolute;color: #5192fe;}/* 控制背景 */.control-back {width: 100vw;height: 80px;bottom: 0;position: absolute;background-color: rgba(255, 255, 255, 0.8);}/* 进度条 */.progress-bar {width: calc(100vw - 100px);margin: 5px 0;padding: 5px 0;cursor: pointer;float: left;}/* 当前播放进度 */.progress-now {width: 0;height: 1px;margin-top: -1px;background-color: #5192fe;}/* 总播放进度 */.progress-all {width: 100%;height: 1px;background-color: #dee2e6;}/* 播放进度 文本 */.time {display: inline-block;width: 90px;height: 21px;margin-left: 5px;text-align: center;color: rgba(128, 130, 133, 0.8);cursor: default;overflow: hidden;}/* 控制按钮 */.control {width: 100%;height: 30px;margin-top: 19px;}.control a {margin: 0 5px;display: inline-block;}.control a:hover {opacity: 50%;}.control-btn {width: 130px;height: 30px;margin: 0 auto;}/* 上一首 */.up {width: 30px;height: 30px;background: url(../img/up.png) no-repeat;background-size: 20px;background-position: 50% 50%;}/* 暂停播放 */.play-pause {width: 30px;height: 30px;background: url(../img/play.png) no-repeat;background-size: 30px;background-position: 50% 50%;}/* 下一首 */.down {width: 30px;height: 30px;background: url(../img/down.png) no-repeat;background-size: 20px;background-position: 50% 50%;}/* 右侧控制按钮 */.control-right {/* width: 180px; */height: 30px;margin-left: calc(50vw + 150px);margin-top: -30px;display: flex;align-items: center;}/* 播放模式 */.mode {width: 30px;height: 30px;background: url(../img/sequence.png) no-repeat;background-size: 20px;background-position: left 50%;cursor: pointer;}/* 声音控制 */.volume {width: 30px;height: 30px;background: url(../img/volume.png) no-repeat;background-size: 20px;background-position: left 50%;cursor: pointer;}.volume-back {padding: 5px 0;cursor: pointer;}.volume-all {width: 100px;height: 2px;background-color: #e5e5e5;}.volume-now {width: 100px;height: 2px;margin-top: -2px;max-width: 100px;background-color: #5192fe;}.volume-text {margin-left: 10px;font-size: 14px;}

JS

var myAduio = document.getElementsByTagName('audio')[0];var divLyrics = document.getElementsByClassName('div-lyrics')[0];var divTitle = document.getElementsByClassName('div-title')[0];var lyricsTime = document.getElementsByClassName('lyrics-time')[0];var lyricsTime_a = lyricsTime.getElementsByTagName('a');var progressTime = document.getElementsByClassName('time')[0];var nowLine = 0;var lyricsMove = false;var playState = false;var lyrics, lyricsStyle, lyricsFirst, rollT;var timeArray1 = new Array();var timeArray2 = new Array();var timeInterval = new Array();window.onload = function () {initialLyrics();lyricsStyle = getComputedStyle(lyricsFirst, null);setLyrics(0);setMouseEvent();setTimeText();};// 设置事件function setMouseEvent() {// 歌词拖拽let lyrics_Y, line;divLyrics.onmousedown = function (e) {if (lyricsMove == false) {lyricsTime_a[0].style.display = lyricsTime_a[1].style.display = lyricsTime.style.display ='block';lyricsMove = true;}lyrics_Y = parseInt(lyricsStyle.marginTop);document.onmousemove = function (event) {lyricsFirst.style.marginTop =event.clientY - (e.clientY - lyrics_Y) + 'px';line = Math.floor(-(parseInt(lyricsStyle.marginTop) - 170) / 34);if (line < 0) {line = 0;} else if (line > lyrics.length - 1) {line = lyrics.length - 1;}lyricsTime_a[1].innerText = timeArray2[line];};document.onmouseup = function () {var lyrics_Y1 = parseInt(lyricsStyle.marginTop);setTimeout(function () {if (parseInt(lyricsStyle.marginTop) == lyrics_Y1) {lyricsMove = false;setLyrics(nowLine);lyricsTime_a[0].style.display = lyricsTime_a[1].style.display = lyricsTime.style.display ='none';}}, 1000);document.onmousemove = null;document.onmouseup = null;};// 防止选中文字return false;};// 音量控制let volume_now = document.getElementsByClassName('volume-now')[0];let volume_back = document.getElementsByClassName('volume-back')[0];let volume_text = document.getElementsByClassName('volume-text')[0];let volume_a = document.getElementsByClassName('volume')[0];volume_back.onmousedown = function (e) {volume_now.style.width = e.offsetX + 'px';myAduio.volume = e.offsetX / 100;volume_text.innerText = volume_now.clientWidth;volume_back.onmousemove = function (ev) {let volume = ev.offsetX;if (volume > 100) {volume = 100;}volume_now.style.width = volume + 'px';myAduio.volume = volume / 100;volume_text.innerText = volume_now.clientWidth;};document.onmouseup = function () {if (myAduio.volume == 0) {volume_a.style.backgroundImage = 'url(../img/mute.png)';} else {volume_a.style.backgroundImage = 'url(../img/volume.png)';}volume_back.onmousemove = null;document.onmouseup = null;};return false;};// 进度控制let progress_now = document.getElementsByClassName('progress-now')[0];let progress_bar = document.getElementsByClassName('progress-bar')[0];progress_bar.onmousedown = function (e) {progress_now.style.width = e.offsetX + 'px';myAduio.pause();myAduio.currentTime =(e.offsetX * myAduio.duration) / progress_bar.clientWidth;setTimeText();progress_bar.onmousemove = function (ev) {let progress = ev.offsetX;if (progress > progress_bar.clientWidth) {progress = progress_bar.clientWidth;}progress_now.style.width = progress + 'px';myAduio.currentTime =(progress * myAduio.duration) / progress_bar.clientWidth;setTimeText();};document.onmouseup = function () {myAduio.play();for (var i = 0; i < timeArray1.length; i++) {if (myAduio.currentTime < timeArray1[i]) {nowLine = i - 1;setLyrics(i - 2);timeInterval[nowLine] =timeArray1[nowLine + 1] - myAduio.currentTime;setPlay(true);break;}}progress_bar.onmousemove = null;document.onmouseup = null;};return false;};let goto = document.getElementsByClassName('goto')[0];goto.onmouseup = function () {nowLine = line;myAduio.currentTime = timeArray1[line];setLyrics(line - 1);setPlay(true);lyricsMove = false;lyricsTime_a[0].style.display = lyricsTime_a[1].style.display = lyricsTime.style.display ='none';document.onmouseup = null;};}// 设置播放状态function setPlay(state) {var play_pause = document.getElementsByClassName('play-pause')[0];if (state == null) {state = myAduio.paused;}clearTimeout(rollT);if (state == true) {myAduio.play();play_pause.style.backgroundImage = 'url(../img/pause.png)';playState = true;setTimeText();lyricsRoll();setProgress();// 开始播放后要重新将时间间距改回来,不然下次播放计时器会出错timeInterval[nowLine] = timeArray1[nowLine + 1] - timeArray1[nowLine];} else {myAduio.pause();play_pause.style.backgroundImage = 'url(../img/play.png)';playState = false;timeInterval[nowLine] = timeArray1[nowLine + 1] - myAduio.currentTime;}}// 设置音量function setVolume(volume) {myAduio.volume = volume;}// 设置静音function setMuted() {let volume_now = document.getElementsByClassName('volume-now')[0];let volume_text = document.getElementsByClassName('volume-text')[0];let volume_a = document.getElementsByClassName('volume')[0];if (myAduio.muted == true) {myAduio.muted = false;volume_a.style.backgroundImage = 'url(../img/volume.png)';volume_now.style.width = myAduio.volume * 100 + 'px';volume_text.innerText = myAduio.volume * 100;} else {myAduio.muted = true;volume_a.style.backgroundImage = 'url(../img/mute.png)';volume_now.style.width = '0';volume_text.innerText = '0';}}// 歌词回弹function lyricsRebound(lyricsTop) {if (parseInt(lyricsStyle.marginTop) != nowLine * -34 + 136) {if (lyricsTop == null) {lyricsTop = nowLine * -34 + 136;}let lyrics_animation = lyricsFirst.animate([{marginTop: lyricsStyle.marginTop,},{marginTop: lyricsTop + 'px',},],{duration: 500,});lyrics_animation.addEventListener('finish',function () {lyricsFirst.style.marginTop = lyricsTop + 'px';},false);}}// 初始化歌词function initialLyrics() {let sp = divTitle.getElementsByTagName('span')[0];let ar = divTitle.getElementsByTagName('span')[1];let ti = divTitle.getElementsByTagName('h1')[0];let lyricsData, timeString;let lyricsArray = new Array();// 清除数组timeArray1.splice(0, timeArray1.length);timeArray2.splice(0, timeArray2.length);lyricsArray.splice(0, lyricsArray.length);// 按相同格式放入歌词更换歌曲即可达到相同效果lyricsData ='[ar]韩安旭\n[ti]不在\n[sp]不在\n[00:00.74]韩安旭 - 不在\n[00:01.76]词:尤雅琪\n[00:02.76]曲:胜屿\n[00:15.62]我累了就紧紧锁住情绪\n[00:18.11]不再放任它堆积\n[00:22.14]我痛了就静静屏住呼吸\n[00:26.02]不给想念留余地\n[00:28.88]只是下雨时会委屈\n[00:32.80]只是想起你会哭泣\n[00:36.77]没关系 真没关系\n[00:44.28]我终于学会一个人弹琴\n[00:47.12]只是弹琴没有你\n[00:49.29]我终于学会一个人做梦\n[00:54.85]只是做梦没有你\n[00:57.73]我依旧像从前粗心\n[01:01.09]时常会忘记星期几\n[01:05.00]却始终忘不掉你看我的眼睛\n[01:11.71]穿过了熙攘的人海\n[01:15.11]想找谁能把你取代\n[01:19.62]复制你曾给过我的\n[01:21.44]那种宠爱\n[01:26.32]掏空了回忆的脑海\n[01:30.75]寂寞却狠狠扑过来\n[01:33.69]措手不及 无法躲开\n[01:41.52]我承认是我太依赖\n[01:44.92]像个不懂事的小孩\n[01:48.35]挥霍掉我们的未来\n[01:51.22]才醒过来\n[01:55.15]我承认后悔了伤害\n[01:59.06]抛开你的好我的坏\n[02:02.14]直到如今学会忍耐 你不在\n[02:26.95]我终于学会一个人弹琴\n[02:29.33]只是弹琴没有你\n[02:33.26]我终于学会一个人做梦\n[02:36.64]只是做梦没有你\n[02:39.53]我依旧像从前粗心\n[02:42.90]时常会忘记星期几\n[02:46.82]却始终忘不掉你看我的眼睛\n[02:53.62]穿过了熙攘的人海\n[02:57.09]想找谁能把你取代\n[03:00.98]复制你曾给过我的\n[03:05.43]那种宠爱\n[03:08.25]掏空了回忆的脑海\n[03:11.67]寂寞却狠狠扑过来\n[03:15.56]措手不及 无法躲开\n[03:22.49]我承认是我太依赖\n[03:26.37]像个不懂事的小孩\n[03:30.38]挥霍掉我们的未来\n[03:33.80]才醒过来\n[03:37.81]我承认后悔了伤害\n[03:41.29]抛开你的好我的坏\n[03:44.73]直到如今学会忍耐 你不在';// 文本.split('分隔符'),用于分割文本lyricsArray = lyricsData.split('\n');// 添加歌曲信息ar.innerText = lyricsArray[0].split(']')[1];ti.innerText = lyricsArray[1].split(']')[1];sp.innerText = lyricsArray[2].split(']')[1];// 添加歌词for (var i = 3; i < lyricsArray.length; i++) {if (i == 3) {divLyrics.innerHTML +='<p style="margin-top: 100%;color:#5192fe;">' +lyricsArray[i].split(']')[1] +'</p>';} else {divLyrics.innerHTML +='<p>' + lyricsArray[i].split(']')[1] + '</p>';}}// 获取后续需要使用的变量lyricsFirst = divLyrics.getElementsByTagName('p')[0];lyrics = divLyrics.getElementsByTagName('p');// 计算每局歌词所在的秒数timeArray1.push(0);for (var i = 0; i < lyrics.length; i++) {timeString = lyricsArray[i + 3].substring(1, 9).split(':');timeArray1.push(parseFloat(timeString[0]) * 60 + parseFloat(timeString[1]));}// 计算时间间隔,将时间从秒数改为分钟+秒数for (var i = 0; i < timeArray1.length - 1; i++) {timeInterval[i] = timeArray1[i + 1] - timeArray1[i];if (Math.floor(timeArray1[i] % 60) < 10) {timeArray2.push(Math.floor(timeArray1[i] / 60) +':0' +Math.floor(timeArray1[i] % 60));} else {timeArray2.push(Math.floor(timeArray1[i] / 60) +':' +Math.floor(timeArray1[i] % 60));}}}// 设置歌词位置function setLyrics(line) {for (let i = 0; i < lyrics.length; i++) {lyrics[i].style.color = '#000';}lyrics[line].style.color = '#5192fe';let lyrics_animation = lyrics[0].animate([{marginTop: lyricsStyle.marginTop,},{marginTop: line * -34 + 136 + 'px',},],{duration: 100,});lyrics_animation.addEventListener('finish',function () {lyrics[0].style.marginTop = line * -34 + 136 + 'px';},false);}// 歌词滚动function lyricsRoll() {rollT = setTimeout(function () {if (nowLine < lyrics.length && myAduio.paused == false) {if (lyricsMove == false) {setLyrics(nowLine);}nowLine += 1;lyricsRoll();}}, timeInterval[nowLine] * 1000);}// 设置进度文本function setTimeText() {var nowTime = myAduio.currentTime;var allTime = myAduio.duration;// 计算时间,若为个位数,补0if (Math.floor(nowTime % 60) < 10) {nowTime = Math.floor(nowTime / 60) + ':0' + Math.floor(nowTime % 60);} else {nowTime = Math.floor(nowTime / 60) + ':' + Math.floor(nowTime % 60);}if (Math.floor(allTime % 60) < 10) {allTime = Math.floor(allTime / 60) + ':0' + Math.floor(allTime % 60);} else {allTime = Math.floor(allTime / 60) + ':' + Math.floor(allTime % 60);}progressTime.innerText = nowTime + '/' + allTime;// 每0.1秒执行一次if (myAduio.paused == false) {setTimeout(setTimeText, 100);}}// 设置进度条进度function setProgress() {let progress_now = document.getElementsByClassName('progress-now')[0];let progress_bar = document.getElementsByClassName('progress-bar')[0];let progress = Math.floor((myAduio.currentTime / myAduio.duration) * progress_bar.clientWidth);progress_now.style.width = progress + 'px';if (myAduio.paused == false) {setTimeout(setProgress, 100);}}// 获取网页属性function getDocument(attribute) {if (attribute == 'sT') {return document.documentElement.scrollTop;} else if (attribute == 'sL') {return document.documentElement.scrollLeft;} else if (attribute == 'sH') {return document.documentElement.scrollHeight;} else if (attribute == 'sW') {return document.documentElement.scrollWidth;} else if (attribute == 'cH') {return document.documentElement.clientHeight;} else if (attribute == 'cW') {return document.documentElement.clientWidth;}}

其中JS中,获取页面属性跟歌词回弹是没有用到的,获取页面属性我写是为了方便,但结果发现没用上,歌词回弹是后来发现直接用设置歌词方法来进行回弹即可,也就是说歌词回弹写了是多余的

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。