Vue全家桶打造自适应 web 音乐播放器

22,098 阅读9分钟

虽然全网上 Vue 仿饿了么、xx音乐的项目一大堆,但是我还是厚着脸皮来了,毕竟我也稍微标新立异,PC端为主,移动端为辅打造的 web 音乐播放器,所以说大佬们关爱下,毕竟我这个播放器刚从韩国回来!!!

模仿QQ音乐网页版界面,采用flexbox和position布局; mmPlayer虽然是响应式,但主要以为PC端为主,移动端只做相应适配(未做歌词显示); 只做主流浏览器兼容(对IE说拜拜并特别优化,想想以前做项目还要兼容IE7,都是泪啊!!!)

api:一个开源的网易云音乐 NodeJS 版 API(有 api 才有动力写!!!)

Vue-mmPlayer 源码地址

在线演示地址 服务器脆弱,小哥哥小姐姐要温柔对待哦(最好是 PC 浏览哦)

如何安装与使用

mmPlayer

git clone https://github.com/maomao1996/Vue-mmPlayer.git  //下载mmPlayer

cd mmPlayer //进入mmPlayer播放器目录

npm install //安装依赖

npm run dev //服务端运行

npm run build  //项目打包

后台服务器

cd mmPlayer/server //进入后台服务器目录

npm install //安装依赖

node app.js //服务端运行 访问 http://localhost:3000

运行mmPlayer后无法获取音乐请检查后台服务器是否启动

api目录下music的url地址要和后台服务器地址一致

技术栈

  • Vue-Cli(Vue 脚手架工具)
  • Vue(核心框架)
  • Vue-Router(页面路由)
  • Vuex(状态管理)
  • ES 6 / 7 (JavaScript 语言的下一代标准)
  • Less(CSS预处理器)
  • Axios(网络请求)
  • FastClick(解决移动端300ms点击延迟)

界面欣赏

PC端界面自我感觉还行, 就是移动端界面总觉得怪怪的,奈何审美有限,所以又去整了高仿网易云的 React 版本(如果小哥哥、小姐姐们有好看的界面,欢迎交流哈)

PC

正在播放

正在播放

排行榜

排行榜

搜索

搜索

我的歌单

我的歌单

我听过的

我听过的

歌曲评论

歌曲评论

移动端

移动端一 移动端二

功能

播放器、快捷键操作、歌词滚动、正在播放、排行榜、歌单详情、搜索、播放历史、查看评论、同步网易云歌单

播放器(核心)

播放器功能其实也就那样,调用 HTML5 音频的方法、属性和事件,网上各种文章也都介绍烂了,但是骗赞要有诚意

我实现的功能有这些:上一曲、下一曲、暂停、播放、单曲循环、列表循环、随机播放、顺序播放、进度条、音量控制

在介绍这些功能之前先理理思路,这里用个小 demo 介绍,毕竟没有代码,虾扯蛋谁不会啊,先上代码

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Audio</title>
	<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
</head>
<body>
	<div id="Audio">
		<audio ref="mmAudio" :src="src" controls></audio>
		<button @click="prev">上一曲</button>
		<button @click="play">暂停/播放</button>
		<button @click="next">下一曲</button>
	</div>
	<script>
		const vm = new Vue({
			el: '#Audio',
			data: {
				list: [
					'https://music.163.com/song/media/outer/url?id=450424527.mp3',
					'https://music.163.com/song/media/outer/url?id=557581284.mp3',
					'https://music.163.com/song/media/outer/url?id=452986458.mp3'
				],//歌曲数组
				index: 0,//当前歌曲下标
			},
			computed: {
				src() {
					return this.list[this.index] // 当前播放歌曲
				}
			},
			methods: {
				prev() {//上一曲
					let index = this.index - 1;
					if (index < 0) {
						index = this.list.length - 1
					}
					this.index = index;
					this.$nextTick(() => this.$refs.mmAudio.play())
				},
				play() {//暂停/播放
					this.$nextTick(() => this.$refs.mmAudio.paused ? this.$refs.mmAudio.play() : this.$refs.mmAudio.pause())
				},
				next() {//下一曲
					let index = this.index + 1;
					if (index === this.list.length) {
						index = 0
					}
					this.index = index;
					this.$nextTick(() => this.$refs.mmAudio.play())
				}
			}
		})
	</script>
</body>
</html>

这段代码的逻辑非常简单,我们会添加一个 computed 动态生成 歌曲 src ,当点击暂停/播放的时候,会调用 play 方法,修改 播放状态;当点击上一曲或者下一曲的时候,会修改当前歌曲播放的 index ,然后会触发 computed 修改 src 然后调用 play 方法播放音乐

看完这个小 demo 这些功能都好理解了

上一曲/下一曲:修改当前歌曲播放的 index ,然后会触发 computed 修改 src ,然后调用 play 方法播放音乐

暂停/播放:通过 Audio 的 paused 属性判断音频是否处于暂停状态,如果返回 true 调用 play 播放音乐,如果返回 false 调用 paused 暂停音乐

单曲循环:调用 Audio 的 ended 事件,在当前歌曲播放结束后将 currentTime 属性重置为 0

列表循环:调用 Audio 的 ended 事件,在其回调中调用下一曲方法

随机播放:通过一个方法打乱歌曲数组,打乱数组前先用一个数组存放原始歌曲数组

顺序播放:调用 Audio 的 ended 事件,判断当前歌曲下标是否和歌曲数组长度 -1 相等,如果相等就不再调用下一曲方法

进度条/音量控制:使用自己封装的 progress 组件 来进行拖动,点击操作来修改对应的播放进度 currentTime 和音量 volume 小记:在 progress 的事件绑定中只有鼠标按下mousedown和触摸开始事件 touchstart 是绑定在对应的 DOM 节点上,其他鼠标移动mousemove和触摸移动touchmove、鼠标释放mouseup和触摸释放touchend等事件绑定的 DOM 都是 document ,不然会出现各种掉链子的问题,比如拖动过程中不小心焦点不在对应的 DOM 上了就会中断拖动

快捷键操作

上一曲 Ctrl + Left

播放暂停 Ctrl + Space

下一曲 Ctrl + Right

切换播放模式 Ctrl + O

音量加 Ctrl + Up

音量减 Ctrl + Down

歌词滚动

歌词滚动原理:根据当前音乐的播放时间 audio.currentTime 去匹配歌词 JSON 数据的时间,然后匹配后的歌词居中显示并高亮

先来张歌词 JSON 的迷人玉照给各位小哥哥、小姐姐们解解馋

{
lyric: "[by:鱼丸啊鱼丸QAQ] [00:00.00] 作曲 : willen [00:01.00] 作词 : 口袋易百 [00:04.60]伴唱:willen [00:05.10]混音/母带:willen [00:55.37]外婆的话 还记得吗 [00:59.01]慈祥的笑容伴我长大 [01:02.75]每当庭院开满了桂花 [01:06.50]淡淡花香都是爱的牵挂 [01:10.30]外婆的话 还记得吗 [01:14.01]受伤的孩子别忘了回家 [01:17.66]夕阳西下 岁月染白了发 "
}

由于 audio.currentTime 是以秒计,而歌词 JSON 的格式是酱样子的 [00:00.00] 所以我们要先把歌词 JSON 化个妆

// 这是化妆过程,具体流程我就不多说了,毕竟我是厚着脸皮来掘金骗小心心的
 function parseLyric(lrc) {
    let lyrics = lrc.split("\n");
    let lrcObj = [];
    for (let i = 0; i < lyrics.length; i++) {
        let lyric = decodeURIComponent(lyrics[i]);
        let timeReg = /\[\d*:\d*((\.|\:)\d*)*\]/g;
        let timeRegExpArr = lyric.match(timeReg);
        if (!timeRegExpArr) continue;
        let clause = lyric.replace(timeReg, '');
        for (let k = 0, h = timeRegExpArr.length; k < h; k++) {
            let t = timeRegExpArr[k];
            let min = Number(String(t.match(/\[\d*/i)).slice(1)),
                sec = Number(String(t.match(/\:\d*/i)).slice(1));
            let time = min * 60 + sec;
            if (clause !== '') {
                lrcObj.push({time: time, text: clause})
            }
        }
    }
    return lrcObj;
}

我比较菜,火星文(正则)全靠搜索引擎,这个是歌词正则原地址,不过我的稍加修饰了下

当这一切 OK 后就只要匹配时间居中并高亮展示当前歌词就行啦! 目前我是通过 for 循环对比大小找到第一个比当前播放时间大的歌词时间,但是我一直觉得这样写不够优雅,无奈又想不到其他方法,希望知道优雅方法的小哥哥、小姐姐来指点迷津

正在播放

显示和管理当前播放的歌单,可以清空当前播放器列表、删除置顶歌曲,修改歌曲的播放状态

排行榜

调用对应 API 接口获取网易云音乐的排行榜列表(目前没做图片懒加载)

歌单详情

传入歌单 ID 调用对应 API 接口获取当前歌单下所有歌曲,由于是获取所有歌曲在移动端滑动时会有所卡顿,这个后期会加入Better-Scroll(一款重点解决移动端各种滚动场景需求的插件)

搜索

目前只实现了歌曲的搜索,后续会完善 专辑 / 歌手 / 歌单 / 用户 的搜索 通过搜索关键字请求 API 获取搜索数据并显示歌曲 分页:调用 scroll 事件,滚动到底部下载下一页,目前是50条每页,当所有数据请求完毕后会提示:没有更多歌曲啦!

在上次网易云音乐和 QQ 音乐版权之争中,周杰伦的所有单曲全部 GG ,然后我就在播放事件中先去请求当前歌曲的 url ,如果没有就会提示:当前歌曲无法播放;如果不做这个,万一又被怼那就不好了,毕竟用户是大佬,惹不起也躲不起

播放历史

调用 canplay 事件,将不会播放出错的歌曲通过 localStorage 存储 PS:一开始我是通过 play 事件 ,结果不管你能不能放都会加入播放历史,然后被吐槽了,最后各种研究发现 canplay 更优雅

查看评论

这个是段子云音乐的一大亮点,必须要加上 通过当前播放音乐的 id 调用对应的 API 接口 分别展示热门评论和最新评论 当时做这个的时候界面简直小意思,但是评论时间简直郁闷,有:

刚刚 / XX 分钟前 / XX:XX / XX 月 XX 日 / XX 年 XX 月 XX 日

大概花了半个多小时才 KO 掉,不过不知道为啥一直觉得我这么写不够优雅,希望知道优雅方法的小哥哥、小姐姐来指点迷津

同步网易云歌单

先去 网易云音乐 获取自己的 UID 然后通过调用对应的 API 接口 获取该用户的歌单,然后传入歌单 ID 获取歌单详情。

当对应的 UID 返回的 playlist 数组长度为 0 时提示 未查询找UID为 ${uid} 的用户信息

登陆成功后调用 localStorage 存储当前 UID ,下次打开时会先读取本地存储的 UID 进行登录操作

退出登录时清空 localStorage ,以免下次打开时还会登录

后续

  1. 代码提高复用率
  2. 优化移动端界面和体验,比如 Better-Scroll图片懒加载
  3. 专辑 / 歌手 / 歌单 / 用户 的搜索
  4. koa 重构后台服务
  5. 完善下桌面版的体验(毕竟太简陋,自己都看不下去了)
  6. ...(脑容量不够,暂时就想到这些)

感谢

HTML 音频/视频 DOM 参考手册

网易云音乐 NodeJS 版 API

Vue.js 升级踩坑小记

Lodash(JavaScript 实用工具库)

其他

个人练手项目,主要有段时间闲得无聊,然后在逛 Gayhub 时发现个开源的音乐API —— 网易云音乐 NodeJS 版 API 最后就一边无耻的水群一边找好看的界面顺带整理下开发思路

曾经有段时间很多人问我用的什么什么 UI 框架,所以我另外再提一下,该项目基础 UI 全是个人结合项目和各大 UI 框架代码风格慢慢敲的,再推荐个 Vue 课程 —— Vue.js 音乐 App 高级实战课程

还有就是有木有 React 大佬带我,最近正在自学中,有很多迷津求解答 这是一边学一边做的项目 React移动端版本(高仿网易云音乐 自我感觉这高仿没毛病

最后我们切入主题,欢迎小哥哥、小姐姐们给我点 "Star" "Fork"(地址在这里 Vue-mmPlayer 源码地址),毕竟第一次发文骗赞(微微一笑),小哥哥、小姐姐们给点鼓励。

如有问题请在本文回复或者直接在 Issues 中提,或者您发现问题并有非常好的解决方案,欢迎 PR