day-096-ninety-six-20230621-仿QQ音乐播放器-静态页面的免费部署-vue2
仿QQ音乐播放器
audio音频标签
-
audio标签
<audio src="images/myDream.m4a" id="audioInp" controls autoplay loop preload="metadata" />
-
标签的属性
- controls:显示浏览器自带的播放组件「表现形式不一样」
- autoplay:自动播放「当代浏览器默认是禁用了自动播放的功能的」
- loop:循环播放
- muted:静音
- preload:资源加载的方式
- none:只有开始播放的时候,才会去加载音频资源(页面加载的时候不会去获取音频资源)
- metadata:页面加载的时候,首先获取音频的一些基本信息
- auto:页面加载的时候,同时加载音频资源
- src:音频的地址
-
各浏览器对不用音频格式的支持是不一样的
-
可以在audio标签用内部使用source来指定可以使用的音频源。
<audio src="images/myDream.m4a" id="audioInp" controls autoplay loop preload="metadata" />
<audio controls> <source src="images/myDream.mp3" type="audio/mpeg" /> <source src="images/myDream.ogg" type="audio/ogg" /> <source src="images/myDream.wav" type="audio/wav" /> 您的浏览器不支持Audio! </audio>
-
-
audio标签对应的DOM元素对象的常见属性
let audioInp = document.querySelector("#audioInp"); console.dir(audioInp)
- currentTime:存储了当前播放的时间「单位秒」
- duration:存储了总的时间
- ended:true/false 是否播放完毕
- paused:true/false 当前是否为暂停的
- volume:0~1 控制音量的 1最大音量 0静音
- play方法:控制音频播放
- pause方法:控制播放暂停
- canplay事件:音频可以播放了
- canplaythrough事件:也是音频可以播放了(加载很多资源后,才会触发,保证音频播放中不卡顿)
- pause事件
- play事件
- playing事件
- volumechange事件
<!DOCTYPE html>
<html>
<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" />
<title>audio音频处理</title>
</head>
<body>
<!--
标签的属性
controls:显示浏览器自带的播放组件「表现形式不一样」
autoplay:自动播放「当代浏览器默认是禁用了自动播放的功能的」
loop:循环播放
muted:静音
preload:资源加载的方式
none:只有开始播放的时候,才会去加载音频资源(页面加载的时候不会去获取音频资源)
metadata:页面加载的时候,首先获取音频的一些基本信息
auto:页面加载的时候,同时加载音频资源
src:音频的地址
各浏览器对不用音频格式的支持是不一样的
-->
<audio
src="https://zxt_team.gitee.io/opensource/qqmusic/mydream.m4a"
id="audioInp"
controls
autoplay
loop
preload="metadata"
/>
<!-- <audio controls>
<source src="images/myDream.mp3" type="audio/mpeg" />
<source src="images/myDream.ogg" type="audio/ogg" />
<source src="images/myDream.wav" type="audio/wav" />
您的浏览器不支持Audio!
</audio> -->
<script>
let audioInp = document.querySelector("#audioInp");
/*
currentTime:存储了当前播放的时间「单位秒」
duration:存储了总的时间
ended:true/false 是否播放完毕
paused:true/false 当前是否为暂停的
volume:0~1 控制音量的 1最大音量 0静音
play方法:控制音频播放
pause方法:控制播放暂停
canplay事件:音频可以播放了
canplaythrough事件:也是音频可以播放了(加载很多资源后,才会触发,保证音频播放中不卡顿)
pause事件
play事件
playing事件
volumechange事件
*/
</script>
</body>
</html>
延期信ajax取数据
const obj = '要返回的数据';
const API = {
queryLyric() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(obj);
}, Math.round(Math.random() * (2000 - 500) + 500));
});
},
};
const obj = {
code: 0,
message: "ok",
data: {
title: "我的梦 - 华为手机主题曲",
author: "张靓颖",
duration: "03:39",
pic: "https://zxt_team.gitee.io/opensource/qqmusic/mydream.jpg",
audio: "https://zxt_team.gitee.io/opensource/qqmusic/mydream.m4a",
lyric:
"[ti:《我的梦》] [ar:张靓颖] [al:] [by:] [offset:0] [00:01.36]我的梦 (华为手机主题曲) - 张靓颖 [00:02.11]词:王海涛/张靓颖 [00:02.64]曲:Andy Love [00:03.48]编曲:崔迪 [00:04.49] [00:08.73]一直地一直地往前走 [00:11.65] [00:13.02]疯狂的世界 [00:14.58] [00:16.68]迎着痛把眼中所有梦 [00:20.52] [00:21.03]都交给时间 [00:22.71] [00:24.24]想飞就用心地去飞 [00:26.98]谁不经历狼狈 [00:30.68] [00:31.60]我想我会忽略失望的灰 [00:34.99]拥抱遗憾的美 [00:39.05]我的梦说别停留等待 [00:43.94]就让光芒折射泪湿的瞳孔 [00:47.74]映出心中最想拥有的彩虹 [00:51.78]带我奔向那片有你的天空 [00:55.74]因为你是我的梦 [01:01.06] [01:07.19]我的梦 [01:08.72] [01:16.75]执着地勇敢地不回头 [01:20.29] [01:21.05]穿过了黑夜踏过了边界 [01:24.87]路过雨路过风往前冲 [01:28.39] [01:28.96]总会有一天站在你身边 [01:32.52]泪就让它往下坠 [01:35.00]溅起伤口的美 [01:38.60] [01:39.55]哦别以为失去的最宝贵 [01:43.00]才让今天浪费 [01:47.04]我的梦说别停留等待 [01:51.93]就让光芒折射泪湿的瞳孔 [01:55.66]映出心中最想拥有的彩虹 [01:59.75]带我奔向那片有你的天空 [02:03.67]因为你是我的梦 [02:09.14] [02:11.72]我的梦 [02:13.09] [02:15.13]我的梦 [02:16.64] [02:19.60]我的梦 [02:21.39] [02:24.27]世界会怎么变化 [02:26.58]都不是意外 [02:28.33]记得用心去回答 [02:30.52]命运的精彩 [02:32.34]世界会怎么变化 [02:34.51]都离不开爱 [02:36.25]记得成长的对话 [02:38.28] [02:39.11]勇敢地说我不再等待 [02:45.63]就让光芒折射泪湿的瞳孔 [02:49.75]映出心中最想拥有的彩虹 [02:53.74]带我奔向那片有你的天空 [02:57.73]因为你是我的梦 [03:02.71] [03:05.51]我的梦 [03:07.32] [03:09.20]我的梦 [03:14.12]因为你是我的梦",
},
};
const API = {
queryLyric() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(obj);
}, Math.round(Math.random() * (2000 - 500) + 500));
});
},
};
对歌词进行处理
let lyric = "[ti:《我的梦》] [ar:张靓颖] [al:] [by:] [offset:0] [00:01.36]我的梦 (华为手机主题曲) - 张靓颖 [00:02.11]词:王海涛/张靓颖 [00:02.64]曲:Andy Love [00:03.48]编曲:崔迪 [00:04.49] [00:08.73]一直地一直地往前走 [00:11.65] [00:13.02]疯狂的世界 [00:14.58] [00:16.68]迎着痛把眼中所有梦 [00:20.52] [00:21.03]都交给时间 [00:22.71] [00:24.24]想飞就用心地去飞 [00:26.98]谁不经历狼狈 [00:30.68] [00:31.60]我想我会忽略失望的灰 [00:34.99]拥抱遗憾的美 [00:39.05]我的梦说别停留等待 [00:43.94]就让光芒折射泪湿的瞳孔 [00:47.74]映出心中最想拥有的彩虹 [00:51.78]带我奔向那片有你的天空 [00:55.74]因为你是我的梦 [01:01.06] [01:07.19]我的梦 [01:08.72] [01:16.75]执着地勇敢地不回头 [01:20.29] [01:21.05]穿过了黑夜踏过了边界 [01:24.87]路过雨路过风往前冲 [01:28.39] [01:28.96]总会有一天站在你身边 [01:32.52]泪就让它往下坠 [01:35.00]溅起伤口的美 [01:38.60] [01:39.55]哦别以为失去的最宝贵 [01:43.00]才让今天浪费 [01:47.04]我的梦说别停留等待 [01:51.93]就让光芒折射泪湿的瞳孔 [01:55.66]映出心中最想拥有的彩虹 [01:59.75]带我奔向那片有你的天空 [02:03.67]因为你是我的梦 [02:09.14] [02:11.72]我的梦 [02:13.09] [02:15.13]我的梦 [02:16.64] [02:19.60]我的梦 [02:21.39] [02:24.27]世界会怎么变化 [02:26.58]都不是意外 [02:28.33]记得用心去回答 [02:30.52]命运的精彩 [02:32.34]世界会怎么变化 [02:34.51]都离不开爱 [02:36.25]记得成长的对话 [02:38.28] [02:39.11]勇敢地说我不再等待 [02:45.63]就让光芒折射泪湿的瞳孔 [02:49.75]映出心中最想拥有的彩虹 [02:53.74]带我奔向那片有你的天空 [02:57.73]因为你是我的梦 [03:02.71] [03:05.51]我的梦 [03:07.32] [03:09.20]我的梦 [03:14.12]因为你是我的梦"
// 1. 歌词解析:
// [ti:《我的梦》]
// [ar:张靓颖]
// [al:]
// [by:]
// [offset:0]
// [00:01.36]我的梦 (华为手机主题曲) - 张靓颖
// [00:02.11]词:王海涛/张靓颖
// [00:02.64]曲:Andy Love
// [00:03.48]编曲:崔迪
// [00:04.49]
// [00:08.73]一直地一直地往前走
// ...
// [03:14.12]因为你是我的梦
// 2. 歌词内容推测:
//   ->空格
// ( ->(
// ) ->)
// - ->-
// ->换行符
// : ->前面的数字是分钟
// . ->前面的数字是秒
// 3. 得到歌词的内容格式。
// 一行歌词:[分钟数:秒数.毫秒数]内部有特殊字符的歌词内容
// 内部有特殊字符的歌词内容:`  为 空格` `( 为 (` `) 为 )` `- 为 -` 。
/* lyric.replace(
/\[(\d+):(\d+).(?:\d+)\]([^&#?]+)(?: )?/g,
(_, $1, $2, $3) => {
console.log($1, $2, $3);
}
); */
// 处理歌词部分的特殊符号
lyric = lyric.replace(/&#(\d+);/g, (value, $1) => {
let instead = value;
switch (+$1) {
case 32:
instead = " ";
break;
case 40:
instead = "(";
break;
case 41:
instead = ")";
break;
case 45:
instead = "-";
break;
default:
}
return instead;
});
// 解析歌词信息
let arr = [];
lyric.replace(
/\[(\d+):(\d+).(?:\d+)\]([^&#?]+)(?: )?/g,
(_, $1, $2, $3) => {
arr.push({
minutes: $1,
seconds: $2,
text: $3,
});
}
);
console.log(arr,lyric)
源码示例
-
JS高级进阶/day0619_QQMusic/index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" /> <title>QQ音乐播放器</title> <!-- IMPORT CSS --> <!-- <link rel="stylesheet" href="css/reset.min.css" /> --> <link rel="stylesheet" href="css/index.css" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js" async></script> </head> <body> <div id="app"> <!-- 头部 --> <header class="header-box"> <div class="base"> <!-- <div class="cover"> <img src="" alt="" /> </div> <div class="info"> <h2 class="title">我的梦 - 华为手机主题曲</h2> <h3 class="author">张靓颖</h3> </div> --> </div> <a href="javascript:;" class="player-button"></a> </header> <!-- 歌词 --> <main class="main-box"> <div class="wrapper"> <!-- <p class="active">我的梦 (华为手机主题曲) - 张靓颖</p> <p>词:王海涛/张靓颖</p> <p>曲:Andy Love</p> --> </div> </main> <!-- 尾部 --> <footer class="footer-box"> <div class="bar"> <span class="time current">00:00</span> <div class="progress"> <div class="already"></div> </div> <span class="time duration">00:00</span> </div> <a href="#" class="download">下载这首音乐</a> </footer> <!-- 其它 --> <div class="mark-image"></div> <div class="mark-overlay"></div> <div class="loading-box"> <div class="content"> <img src="images/loading.gif" alt="" /> <span>奴家正在努力加载中...</span> </div> </div> <audio src="" id="audioBox" preload="metadata"></audio> </div> <!-- IMPORT JS --> <script> (function () { const app = document.querySelector("#app"), HTML = document.documentElement; const computed = function computed() { let orien = Math.abs(window.orientation); if (orien === 90) { // 切换到横屏 alert("横屏体验不佳,请您切换到竖屏"); app.style.display = "none"; return; } // 当前是竖屏 app.style.display = "block"; // 计算REM的换算比例 let deviceW = HTML.clientWidth, designW = 375, initialRatio = 100; if (deviceW > 540) deviceW = 540; let currentRatio = (deviceW / designW) * initialRatio; HTML.style.fontSize = currentRatio + "px"; }; computed(); window.addEventListener("resize", computed); // 基于 resize 代替 orientationchange 事件:这样可以适配模拟器中,设备的切换场景 })(); </script> <script src="js/api.js"></script> <script src="https://cdn.jsdelivr.net/npm/fastclick@1.0.6/lib/fastclick.min.js"></script> <script src="js/index.js"></script> </body> </html>
-
JS高级进阶/day0619_QQMusic/css/index.css
-
由less文件编译而来
html { font-size: 100px; } html, body, #app { height: 100%; overflow: hidden; } #app { position: relative; margin: 0 auto; max-width: 540px; font-size: 0.14rem; } .header-box, .footer-box, .main-box { box-sizing: border-box; height: 1rem; overflow: hidden; } .main-box { height: calc(100vh - 2rem); } /* 通用样式 */ .text-clip { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } /* Loading层 */ .loading-box { position: fixed; top: 0; left: 0; z-index: 9999; box-sizing: border-box; width: 100vw; height: 100vh; background: #555; display: flex; justify-content: center; align-items: center; } .loading-box .content img, .loading-box .content span { display: block; } .loading-box .content img { margin: 0 auto; width: 0.5rem; height: 0.5rem; } .loading-box .content span { margin-top: 0.1rem; color: #1989fa; } /* 背景层 */ .mark-overlay, .mark-image { position: absolute; top: -10%; left: -10%; width: 120%; height: 120%; } .mark-image { z-index: -2; background-repeat: no-repeat; background-size: cover; filter: blur(6px); } .mark-overlay { z-index: -1; background: rgba(0, 0, 0, 0.5); } /* 头部区域样式 */ .header-box { display: flex; justify-content: space-between; align-items: center; padding: 0.15rem; } .header-box .player-button { margin-left: 0.05rem; width: 0.35rem; height: 0.35rem; background: url('../images/music.svg') no-repeat; background-size: 100% 100%; } .header-box .player-button.move { animation: musicMove 1s linear 0s infinite both; } .header-box .base { flex-grow: 1; display: flex; } .header-box .base .cover { width: 0.7rem; height: 0.7rem; background: #AAA; } .header-box .base .cover img { display: block; width: 100%; height: 100%; } .header-box .base .cover img[src=""] { display: none; } .header-box .base .info { flex-grow: 1; margin-left: 0.05rem; max-width: 2.3rem; } .header-box .base .info .title, .header-box .base .info .author { line-height: 0.35rem; color: #fff; font-size: 0.17rem; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } .header-box .base .info .author { font-size: 0.15rem; } @keyframes musicMove { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } /* 歌词区域样式 */ .main-box .wrapper { transform: translateY(0); transition: transform 0.3s; } .main-box .wrapper p { height: 0.5rem; line-height: 0.5rem; text-align: center; font-size: 0.15rem; color: rgba(255, 255, 255, 0.5); } .main-box .wrapper p.active { color: #31C27C; transition: color 0.3s; } /* 尾部区域样式 */ .footer-box { padding: 0 0.1rem; } .footer-box .download { display: block; margin: 0 auto; width: 2.13rem; height: 0.5rem; line-height: 0.5rem; text-align: center; font-size: 0.18rem; color: #fff; text-indent: 0.2rem; border-radius: 0.25rem; background: url('../images/sprite_play.png') no-repeat #31C27C; background-size: 0.4rem 3.5rem; background-position: 0.1rem -2.915rem; } .footer-box .bar { display: flex; align-items: center; } .footer-box .bar .time { width: 0.4rem; line-height: 0.46rem; text-align: center; font-size: 0.12rem; color: rgba(255, 255, 255, 0.5); } .footer-box .bar .progress { position: relative; flex-grow: 1; height: 0.02rem; background: rgba(255, 255, 255, 0.5); } .footer-box .bar .progress .already { position: absolute; top: 0; left: 0; width: 0; height: 100%; background: #31C27C; } /* 音频 */ #audioBox { display: none; }
-
-
JS高级进阶/day0619_QQMusic/css/index.less
html { font-size: 100px; //1REM=100PX「在375px的设计稿中」 } html, body, #app { height: 100%; overflow: hidden; } #app { position: relative; margin: 0 auto; max-width: 540px; font-size: .14rem; } .header-box, .footer-box, .main-box { box-sizing: border-box; height: 1rem; overflow: hidden; } .main-box { height: calc(100vh - 2rem); } /* 通用样式 */ @com-green: #31C27C; @com-gray: rgba(255, 255, 255, .5); .text-clip { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } /* Loading层 */ .loading-box { position: fixed; top: 0; left: 0; z-index: 9999; box-sizing: border-box; width: 100vw; height: 100vh; background: #555; display: flex; justify-content: center; align-items: center; .content { img, span { display: block; } img { margin: 0 auto; width: .5rem; height: .5rem; } span { margin-top: .1rem; color: rgb(25, 137, 250); } } } /* 背景层 */ .mark-overlay, .mark-image { position: absolute; top: -10%; left: -10%; width: 120%; height: 120%; } .mark-image { z-index: -2; background-repeat: no-repeat; background-size: cover; filter: blur(6px); // 设置模糊度 } .mark-overlay { z-index: -1; background: rgba(0, 0, 0, .5); } /* 头部区域样式 */ .header-box { display: flex; justify-content: space-between; align-items: center; padding: .15rem; .player-button { margin-left: .05rem; width: .35rem; height: .35rem; background: url('../images/music.svg') no-repeat; background-size: 100% 100%; &.move { animation: musicMove 1s linear 0s infinite both; } } .base { flex-grow: 1; display: flex; .cover { width: .7rem; height: .7rem; background: #AAA; img { display: block; width: 100%; height: 100%; } img[src=""] { display: none; } } .info { flex-grow: 1; margin-left: .05rem; max-width: 2.3rem; .title, .author { line-height: .35rem; color: #fff; font-size: .17rem; .text-clip; } .author { font-size: .15rem; } } } } @keyframes musicMove { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } /* 歌词区域样式 */ .main-box { .wrapper { transform: translateY(0); transition: transform .3s; p { height: .5rem; line-height: .5rem; text-align: center; font-size: .15rem; color: @com-gray; &.active { color: @com-green; transition: color .3s; } } } } /* 尾部区域样式 */ .footer-box { padding: 0 .1rem; .download { display: block; margin: 0 auto; width: 2.13rem; height: .5rem; line-height: .5rem; text-align: center; font-size: .18rem; color: #fff; text-indent: .2rem; border-radius: .25rem; background: url('../images/sprite_play.png') no-repeat @com-green; background-size: .4rem 3.5rem; background-position: .1rem -2.915rem; } .bar { display: flex; align-items: center; .time { width: .4rem; line-height: .46rem; text-align: center; font-size: .12rem; color: @com-gray; } .progress { position: relative; flex-grow: 1; height: .02rem; background: @com-gray; .already { position: absolute; top: 0; left: 0; width: 0; height: 100%; background: @com-green; } } } } /* 音频 */ #audioBox { display: none; }
-
JS高级进阶/day0619_QQMusic/js/api.js
const obj = { code: 0, message: "ok", data: { title: "我的梦 - 华为手机主题曲", author: "张靓颖", duration: "03:39", pic: "https://zxt_team.gitee.io/opensource/qqmusic/mydream.jpg", audio: "https://zxt_team.gitee.io/opensource/qqmusic/mydream.m4a", lyric: "[ti:《我的梦》] [ar:张靓颖] [al:] [by:] [offset:0] [00:01.36]我的梦 (华为手机主题曲) - 张靓颖 [00:02.11]词:王海涛/张靓颖 [00:02.64]曲:Andy Love [00:03.48]编曲:崔迪 [00:04.49] [00:08.73]一直地一直地往前走 [00:11.65] [00:13.02]疯狂的世界 [00:14.58] [00:16.68]迎着痛把眼中所有梦 [00:20.52] [00:21.03]都交给时间 [00:22.71] [00:24.24]想飞就用心地去飞 [00:26.98]谁不经历狼狈 [00:30.68] [00:31.60]我想我会忽略失望的灰 [00:34.99]拥抱遗憾的美 [00:39.05]我的梦说别停留等待 [00:43.94]就让光芒折射泪湿的瞳孔 [00:47.74]映出心中最想拥有的彩虹 [00:51.78]带我奔向那片有你的天空 [00:55.74]因为你是我的梦 [01:01.06] [01:07.19]我的梦 [01:08.72] [01:16.75]执着地勇敢地不回头 [01:20.29] [01:21.05]穿过了黑夜踏过了边界 [01:24.87]路过雨路过风往前冲 [01:28.39] [01:28.96]总会有一天站在你身边 [01:32.52]泪就让它往下坠 [01:35.00]溅起伤口的美 [01:38.60] [01:39.55]哦别以为失去的最宝贵 [01:43.00]才让今天浪费 [01:47.04]我的梦说别停留等待 [01:51.93]就让光芒折射泪湿的瞳孔 [01:55.66]映出心中最想拥有的彩虹 [01:59.75]带我奔向那片有你的天空 [02:03.67]因为你是我的梦 [02:09.14] [02:11.72]我的梦 [02:13.09] [02:15.13]我的梦 [02:16.64] [02:19.60]我的梦 [02:21.39] [02:24.27]世界会怎么变化 [02:26.58]都不是意外 [02:28.33]记得用心去回答 [02:30.52]命运的精彩 [02:32.34]世界会怎么变化 [02:34.51]都离不开爱 [02:36.25]记得成长的对话 [02:38.28] [02:39.11]勇敢地说我不再等待 [02:45.63]就让光芒折射泪湿的瞳孔 [02:49.75]映出心中最想拥有的彩虹 [02:53.74]带我奔向那片有你的天空 [02:57.73]因为你是我的梦 [03:02.71] [03:05.51]我的梦 [03:07.32] [03:09.20]我的梦 [03:14.12]因为你是我的梦", }, }; const API = { queryLyric() { return new Promise((resolve) => { setTimeout(() => { resolve(obj); }, Math.round(Math.random() * (2000 - 500) + 500)); }); }, };
-
JS高级进阶/day0619_QQMusic/js/index.js
// 解决 click 事件的300ms延迟问题 FastClick.attach(document.body); console.log(`cdn-->`); (async function () { const baseBox = document.querySelector(".header-box .base"), playerButton = document.querySelector(".player-button"), wrapperBox = document.querySelector(".wrapper"), footerBox = document.querySelector(".footer-box"), currentBox = footerBox.querySelector(".current"), durationBox = footerBox.querySelector(".duration"), alreadyBox = footerBox.querySelector(".already"), markImageBox = document.querySelector(".mark-image"), loadingBox = document.querySelector(".loading-box"), audioBox = document.querySelector("#audioBox"); let wrapperList = [], timer = null, matchNum = 0; //记录历史匹配的数量 /* 音乐控制 */ const format = function format(time) { let minutes = Math.floor(time / 60), seconds = Math.round(time - minutes * 60); minutes = minutes < 10 ? "0" + minutes : "" + minutes; seconds = seconds < 10 ? "0" + seconds : "" + seconds; return { minutes, seconds, }; }; const playend = function playend() { clearInterval(timer); timer = null; currentBox.innerHTML = "00:00"; alreadyBox.style.width = "0%"; wrapperBox.style.transform = "translateY(0)"; wrapperList.forEach((item) => (item.className = "")); matchNum = 0; playerButton.className = "player-button"; }; const handle = function handle() { let pH = wrapperList[0].offsetHeight; let { currentTime, duration } = audioBox; if (isNaN(currentTime) || isNaN(duration)) return; // 播放结束 if (currentTime >= duration) { playend(); return; } // 控制进度条 let { minutes: currentTimeMinutes, seconds: currentTimeSeconds } = format(currentTime), { minutes: durationMinutes, seconds: durationSeconds } = format(duration), ratio = Math.round((currentTime / duration) * 100); currentBox.innerHTML = `${currentTimeMinutes}:${currentTimeSeconds}`; durationBox.innerHTML = `${durationMinutes}:${durationSeconds}`; alreadyBox.style.width = `${ratio}%`; // 控制歌词:查找和当前播放时间匹配的歌词段落 let matchs = wrapperList.filter((item) => { let minutes = item.getAttribute("minutes"), seconds = item.getAttribute("seconds"); return minutes === currentTimeMinutes && seconds === currentTimeSeconds; }); if (matchs.length > 0) { // 让匹配的段落有选中样式,而其余的移除选中样式 wrapperList.forEach((item) => (item.className = "")); matchs.forEach((item) => (item.className = "active")); // 控制移动 matchNum += matchs.length; if (matchNum > 3) { let offset = (matchNum - 3) * pH; wrapperBox.style.transform = `translateY(${-offset}px)`; } } }; playerButton.addEventListener("click", function () { if (audioBox.paused) { // 当前是暂停的:我们让其播放 audioBox.play(); playerButton.className = "player-button move"; handle(); if (!timer) timer = setInterval(handle, 1000); return; } // 当前是播放的:我们让其暂停 audioBox.pause(); playerButton.className = "player-button"; clearInterval(timer); timer = null; }); /* 绑定数据 */ const bindLyric = function bindLyric(lyric) { // 处理歌词部分的特殊符号 lyric = lyric.replace(/&#(\d+);/g, (value, $1) => { let instead = value; switch (+$1) { case 32: instead = " "; break; case 40: instead = "("; break; case 41: instead = ")"; break; case 45: instead = "-"; break; default: } return instead; }); // 解析歌词信息 let arr = []; lyric.replace( /\[(\d+):(\d+).(?:\d+)\]([^&#?]+)(?: )?/g, (_, $1, $2, $3) => { arr.push({ minutes: $1, seconds: $2, text: $3, }); } ); // 歌词绑定 let str = ``; arr.forEach(({ minutes, seconds, text }) => { str += `<p minutes="${minutes}" seconds="${seconds}"> ${text} </p>`; }); wrapperBox.innerHTML = str; // 获取所有的P标签 wrapperList = Array.from(wrapperBox.querySelectorAll("p")); }; const binding = function binding(data) { let { title, author, duration, pic, audio, lyric } = data; // @1 绑定头部基本信息 baseBox.innerHTML = ` <div class="cover"> <img src="${pic}" alt=""> </div> <div class="info"> <h2 class="title">${title}</h2> <h3 class="author">${author}</h3> </div> `; // @2 杂七杂八的信息 durationBox.innerHTML = duration; markImageBox.style.backgroundImage = `url(${pic})`; audioBox.src = audio; // @3 绑定歌词信息 bindLyric(lyric); // @4 关闭Loading效果 loadingBox.style.display = "none"; }; /* 向服务器发送请求,从服务器获取相关的数据 */ try { let { code, data } = await API.queryLyric(); if (+code === 0) { // 请求成功:网络层和业务层都成功 binding(data); return; } } catch (_) {} // 请求失败 alert("网络繁忙,请刷新页面"); })();
静态页面的免费部署
gitee部署项目
- 部署项目:gitee
github部署项目
- 第二种方案:基于github部署
vue2
-
组件化开发:文章来源:https://uudwc.com/A/P5oAb
- 一个页面比较大,我们把页面按照功能板块拆分为一个个的组件,最后把组件合并在一起
- 在SPA单页面应用开发中,真正的页面只有一个,我们把所谓的页面都设计为一个个的组件,最后基于路由管控机制,控制哪些组件渲染,哪些组件销毁
-
脚手架:把webpack相关的配置规则都处理好了,把一个工程化组件化的项目架子都搭建完毕了文章来源地址https://uudwc.com/A/P5oAb
- Vue框架:
- 以webpack为核心的脚手架 @vue/cli
- 创建vue2的项目
- 创建vue3的项目
- 新的脚手架 vite
- 创建vue3的项目
- 以webpack为核心的脚手架 @vue/cli
- Vue框架:
进阶参考
- FastClick-cdn
- prefixfree-cdn