主頁 > 前端設計 > 使用HTML+CSS+JS做一個音樂播放器

使用HTML+CSS+JS做一個音樂播放器

2021-02-16 15:12:25 前端設計

一、觀前說明:
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;
	// 計算時間,若為個位數,補0
	if (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) +136
	let 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,所以在設定音量時要將其除以100
	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 () {
			// 如果音量為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 - 2
					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;
	};

	// 拖拽歌詞調整進度,需要注意的也只是算出用戶拖到了第幾行而已,在拖拽歌詞事件中(在上面)我們就已經將行數算出來了,這里直接呼叫就可用
	// 也說一下演算法,因為我們是按照行來設定第一行歌詞的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;
	// 計算時間,若為個位數,補0
	if (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中,獲取頁面屬性跟歌詞回彈是沒有用到的,獲取頁面屬性我寫是為了方便,但結果發現沒用上,歌詞回彈是后來發現直接用設定歌詞方法來進行回彈即可,也就是說歌詞回彈寫了是多余的

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/260078.html

標籤:其他

上一篇:觀察者模式實作表單驗證

下一篇:淺析Node中間件Koa&Express:原理和實作

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more