用户首先访问的是Home.vue组件,该组件在加载完成的时候调用created方法获取全部的歌单

(getSongsMenu)和歌曲(getAllSinger),   并将全部的歌单和歌手存放到了

songsMenuAndSinger中,这是个数组,里面存放的是两个对象,一个是歌单(每个歌单对象由多个songsMenuSong对象构成,每个songsMenuSong对象中包含一个song对象)对象另一个是歌曲对象。歌单和歌曲对象中又分别含有一个数组,用来分别存放歌单和歌曲。如下:

songsMenuAndSinger: [{name: '推荐歌单>', list: []},{name: '热门歌手>', list: []}
]

此时,在Home.vue页面遍历songsMenuAndSinger数组。

<div class="section" v-for="(item, index) in songsMenuAndSinger" :key="index"><div class="section-title"><b>{{item.name}}</b></div><content-list :contentList=item.list></content-list></div>

其中item就指的是songsMenuAndSinger数组中的两个对象{name: '推荐歌单>', list: [ ]}{name: '热门歌手>', list: []},然后将list传递给了ContentList.vue组件,由该组件进一步遍历。

props: ['contentList'
],

也就是说Home.vue组件中item.list传递到了contentList中。

 <li class="content-item" v-for="(item, index) in contentList" :key="index"><div class="kuo" @click="goAblum(item, item.introduce)"><img class="item-img" :src="(item.picture)" alt=""><div class="mask"  @click="goAblum(item, item.name)"></div></div><p class="item-name"><b>{{item.name || item.title}}</b></p></li>

这里的item代表的就是歌单和歌手对象了,第一次遍历的时候是歌单,第二次遍历的时候是歌手。因为歌单个歌手的是通过introduce字段区分的,因此如果当前是歌单的话会传递introduce参数,如果是歌手的话,会传递歌手的名字name参数。(下面以歌手为例,假设是第二次遍历,contentList中全是纯歌手对象)。

在点击歌手的头像后触发goAblum方法。

goAblum (item, type) {//将单个歌手或者是歌单放到缓存中,便于在页面跳转的时候取出。this.$store.commit('setSingerOrMenuTemp', item)if (type) {  //转向歌手页面this.$router.push({path: `/singer-album/${item.id}`})} else {    //转向歌单页面this.$router.push({path: `/song-list-album/${item.id}`})}
}

先将获取的该歌手对象保存到缓存singerOrMenuTemp中,当前是歌手触发的方法的话就保存的是一个歌手对象,反之,是一个歌单对象。然后携带个当前歌手(歌单)对象的参数id跳转到singerAlbum.vue页面中。

mounted () {this.singerId = this.$route.params.id // 从路由中获取歌手idthis.singerOrMenu = this.singerOrMenuTemp   //从缓存获取歌手的信息this.getSongList()}

singerAlbum.vue页面一加载就获取路径参数id和缓存中存储的歌手(歌单)对象,其中获取id的目的是为了根据id查歌单或者是歌手的全部歌曲,在滴哦用this.getSongList()方法时用到了,如下。

 getSongList () {getSongOfSingerId(this.singerId).then(res => {//这是要在播放列表显示的歌曲this.songs = resthis.$store.commit("setListOfSongs",res)}).catch(err => {console.log(err)})}

然后就将获取的某个歌手或者是歌单的全部歌曲放到缓存中去了。此外将查询结果展示在当前页面上遍历获取的全部歌曲,如下.

<album-content :songList=this.songs> </album-content>

然后将获取的全部歌曲传递到了AlbumContent.vue页面,在该页面中遍历获取的歌曲,如下。

<li class="list-content" v-for="(item, index) in songList" :key="index"><div class="song-item" :class="{'is-play': id === item.id}"  @click="toplay(item.id, item.url, item.picture, index, item.name, item.lyrics)"><span class="item-index"><span v-if="id !== item.id">{{index + 1}}</span><svg v-if="id === item.id" class="icon" aria-hidden="true"><use xlink:href="#icon-yinliang"></use></svg></span><span class="item-title">{{replaceFName(item.name)}}</span><span class="item-name">{{replaceLName(item.name)}}</span><span class="item-intro">{{item.introduction}}</span><span class="item-intro">{{item.introduction}}</span></div></li>

如果是歌单的话稍微有些不同,因为每个歌单中的歌曲(也就是songsMenuSong中包含一个song对象)如下图。

  <li class="list-content" v-for="(item, index) in songList" :key="index"><div class="song-item" :class="{'is-play': id === item.id}"  @click="toplay(item.song.id, item.song.url, item.song.picture, index, item.song.name, item.song.lyrics)"><span class="item-index"><span v-if="id !== item.id">{{index + 1}}</span><svg v-if="id === item.id" class="icon" aria-hidden="true"><use xlink:href="#icon-yinliang"></use></svg></span><span class="item-title">{{item.song.name}}</span><span class="item-name">{{item.song.name}}</span><span class="item-intro">{{item.song.introduction}}</span><span class="item-intro">时长</span></div></li>

此时,songList中存放的就是一个个的song对象了,:class="{'is-play': id === item.id}"表示如果缓存中的id和歌曲id一样的话激活该calss样式。

@click="toplay(item.id, item.url, item.picture, index, item.name, item.lyrics)调用的是minix中的播放方法,参数为当前歌曲的id,当前歌曲的播放地址,当前歌曲的图片地址,当前歌曲在当前歌曲列表中的索引位置,当前歌曲的名字和歌词。

当用户点击播放时,会触发toPlay方法,如下。

 toplay: function (id, url, picture, index, name, lyrics) {this.$store.commit('setId', id)this.$store.commit('setListIndex', index)this.$store.commit('setUrl', url)this.$store.commit('setpicUrl', picture)this.$store.commit('setTitle', this.replaceFName(name))this.$store.commit('setArtist', this.replaceLName(name))this.$store.commit('setLyric', this.parseLyric(lyrics))if (this.loginIn) {this.$store.commit('setIsActive', false)getCollectionOfUser(this.userId).then(res => {for (let item of res) {if (item.songId === id) {this.$store.commit('setIsActive', true)break}}}).catch(err => {console.log(err)})}},

调用播放方法其实就是将这些数据存储到缓存中,this.loginIn是在用户登录后执行的,SongAudio.vue组件是根据url播放歌曲的,一旦缓存中的url改变,SongAudio.vue组件中播放的歌曲也会随着改变。如下。

<audio :src='url' controls="controls"ref="player" preload="true"@canplay="startPlay"@timeupdate="timeupdate"@ended="ended"></audio>

至此,点击播放列表中的歌曲可以切换播放。


接下来就是画一个好看的播放器了。就是playBar.vue组件。

 <div ref="progress" class="progress" @mousemove="mousemove"><!--进度条--><div ref="bg" class="bg" @click="updatemove"><div ref="curProgress" class="cur-progress" :style="{width: curLength+'%'}"></div></div><!--进度条 end --><!--拖动的点点--><div ref="idot" class="idot" :style="{left: curLength+'%'}" @mousedown="mousedown" @mouseup="mouseup"></div><!--拖动的点点 end --></div>

 progress指的是当前整个的进度条,curProgress指的是当前歌曲播放经过的区域,idotcurProgress的值是相同的都表示的是经过的区域。

点击播放按钮会触发事件togglePlay,判断取出缓存中的值isPlay,缓存中的isPlay默认是false,也就是不播放状态,执行else语句更新缓存中的isPlay为true,还要接着更新播放按钮的状态。如下

 // 控制音乐播放 / 暂停togglePlay () {if (this.isPlay) {this.$store.commit('setIsPlay', false)} else {this.$store.commit('setIsPlay', true)}},
   //监控isPlay改变播放器的状态isPlay(){if(this.isPlay){this.$store.commit('setPlayButtonUrl', '#icon-zanting')}else{this.$store.commit('setPlayButtonUrl', '#icon-bofang')}},

进度条的拖拽原理:

首先需要在播放器页面也就是playbar.vue先计算好进度条的总长度如下:

this.progressLength = this.$refs.progress.getBoundingClientRect().width      //获取进度条的长度

当前播放歌曲的时间占歌曲总时长的百分比=当前歌曲的进度条占总进度条的百分比。如下,是当前播放时间占歌曲总时间的百分比。

this.curLength = (this.curTime / this.duration) * 100

如下,是歌曲的进度条占总进度条的百分比。点击事件 @click="updatemove"是拖动点点播放歌曲的时候触发该事件。

  <!--进度条--><div ref="bg" class="bg" @click="updatemove"><div ref="curProgress" class="cur-progress" :style="{width: curLength+'%'}"></div></div>

如下,是点点在进度条上的偏移量。

  <!--拖动的点点--><div ref="idot" class="idot" :style="{left: curLength+'%'}" @mousedown="mousedown" @mouseup="mouseup"></div><!--拖动的点点 end -->

鼠标点击点点的时候会使变量tag变为true ,并且获得开始移动的点点的X坐标,如下。

 mousedown (e) {this.mouseStartX = e.clientXthis.tag = true},

当tag变为true的时候就说明当前是拖动点点的状态,进度条的位置也要随着改变,进度条从点点被点击开始算,为

let movementX = e.clientX - this.mouseStartX

其中 e.clientX表示的是当前的点点的位置,mouseStartX表示的是刚开始点点移动的位置两者的差就是点点的位移。还要加上点点还没移动之前的进度条的长度,

this.progressLength = this.$refs.progress.getBoundingClientRect().width

两者相加就是当前的进度条的长度和点点的位置。

let newPercent = ((curLength + movementX) / this.progressLength) * 100
this.curLength = newPercent
this.mouseStartX = e.clientX  //重新为当前点点的位置赋值。
  mousedown (e) {this.mouseStartX = e.clientXthis.tag = true},
 // 拖拽中mousemove (e) {if (!this.duration) {return false}if (this.tag) {let movementX = e.clientX - this.mouseStartXlet curLength = this.$refs.curProgress.getBoundingClientRect().width//  计算出百分比this.progressLength = this.$refs.progress.getBoundingClientRect().widthlet newPercent = ((curLength + movementX) / this.progressLength) * 100if (newPercent > 100) {newPercent = 100}this.curLength = newPercentthis.mouseStartX = e.clientX  //重新为当前点点的位置赋值。//  根据百分比推出对应的播放时间this.changeTime(newPercent)}},

然后还要改变播放时间, 就是根据当前的newPercent (也就是当前的进度占总进度条的长度的百分比)来更新时间,如下。

 // 更改歌曲进度changeTime (percent) {let newCurTime = this.duration * (percent * 0.01)this.$store.commit('setChangeTime', newCurTime)},

另外时间的格式需要转化成时分秒的形式,如下。

 // 播放时间的开始和结束curTime () {this.nowTime = this.formatSeconds(this.curTime)this.songTime = this.formatSeconds(this.duration)// 移动进度条this.curLength = (this.curTime / this.duration) * 100},
    // 解析播放时间formatSeconds (value) {let theTime = parseInt(value)let theTime1 = 0let theTime2 = 0if (theTime > 60) {theTime1 = parseInt(theTime / 60) // 分theTime = parseInt(theTime % 60) // 秒// 是否超过一个小时if (theTime1 > 60) {theTime2 = parseInt(theTime1 / 60) // 小时theTime1 = 60 // 分}}// 多少秒if (parseInt(theTime) < 10) {var result = '0:0' + parseInt(theTime)} else {result = '0:' + parseInt(theTime)}// 多少分钟时if (theTime1 > 0) {if (parseInt(theTime) < 10) {result = '0' + parseInt(theTime)} else {result = parseInt(theTime)}result = parseInt(theTime1) + ':' + result}// 多少小时时if (theTime2 > 0) {if (parseInt(theTime) < 10) {result = '0' + parseInt(theTime)} else {result = parseInt(theTime)}result = parseInt(theTime2) + ':' + parseInt(theTime1) + ':' + result}return result},

音量控件就是个图标加滑块,就是在mounted中设置监听图标点击事件,监听到了之后弹出滑块,在watch中监听滑块的值,并更新到缓存中,如下。

 document.querySelector('.icon-volume').addEventListener('click', function (e) {document.querySelector('.volume').classList.add('show-volume')e.stopPropagation()}, false)document.querySelector('.volume').addEventListener('click', function (e) {e.stopPropagation()}, false)document.addEventListener('click', function () {document.querySelector('.volume').classList.remove('show-volume')}, 
  volume () {this.$store.commit('setVolume', this.volume / 100)},

歌曲列表的组件是个公共组件,因此写到了compoents里面。 歌曲列表就是当前的歌曲列表只不过就是能在其他页面显示罢了(这里出现bug),,如下:

<template><transition name="slide-fade"><div class="the-aside" v-if="this.showAside"><h2 class="title">播放列表</h2><ul class="menus"><li v-for="(item, index) in listOfSongs" :class="{'is-play': id=== item.id}" :key="index" @click="toplay(item.id,item.url,item.picture,index,item.name,item.lyrics)">{{replaceFName(item.name)}}</li></ul></div></transition>
</template>

上一曲和下一曲就是在获取当前歌曲在歌单中的位置的基础上索引的加减 ,以上一首为例,如下。

  prev () {if (this.listIndex !== -1 && this.listOfSongs.length > 1) { //当前歌单列表的数量是大于1的才会切换if (this.listIndex > 0) {   //不是第一首音乐this.$store.commit('setListIndex', this.listIndex - 1)  //将上一首歌曲在播放列表中的位置存放到缓存中。this.toPlay(this.listOfSongs[this.listIndex].url)         //播放当前的歌曲} else {this.$store.commit('setListIndex', this.listOfSongs.length - 1)this.toPlay(this.listOfSongs[this.listIndex].url)//切换到倒数第一首音乐,放到缓存中。}}},

当前歌曲播放完成后自动播放下一首歌曲,触发事件是在SongAudio.vue 中的end方法,该方法中改变autoNext的值,playBar组件中监控这个值的变化,只要改变就调用播放下一首的方法,如下。

 // 音乐播放结束时触发ended () {this.$store.commit('setIsPlay', false)this.$store.commit('setCurTime', 0)this.$store.commit('setAutoNext', !this.autoNext)}
// 自动播放下一首autoNext () {this.next()}
 // 下一首next () {if (this.listIndex !== -1 && this.listOfSongs.length > 1) {if (this.listIndex < this.listOfSongs.length - 1) {this.$store.commit('setListIndex', this.listIndex + 1)this.toPlay(this.listOfSongs[this.listIndex].url)} else {//否则就是最后一首(也就是第一首)this.$store.commit('setListIndex', 0)this.toPlay(this.listOfSongs[0].url)}}

至此整个播放器完毕。


对于歌词的解析,因为后台存入的歌词是带有时间 的如下,

[01:22.290] 海上清辉与圆月 盛进杯光

将所有歌词看作是一个数组,每行歌词是数组的一个数据。然后用正则表达式将数组中每个元素的时间和歌词分离。也就是如下格式:

【【时间,歌词】,【时间,歌词】】

vue音乐播放器笔记相关推荐

  1. vue 音乐播放器上一首 下一首切换

    vue 音乐播放器上一首 下一首切换 根据自定义属性的值找到元素 我是使用监听来实现切换的,将v-for循环列表的index存储在vuex中,点击上一首或下一首改变index的值,在另一个组件中监听i ...

  2. 【VUE音乐播放器】获取QQ音乐播放源地址

    前言:在学习慕课网课程制作企业级音乐app中,发现之前的qq音乐播放地址均不可用了.于是折腾了一下,重新抓包获取地址. 1.获取分类歌单里面的歌曲列表 在之前的代码里,如下红色圆中的歌单-歌曲列表显示 ...

  3. 一个月写完vue音乐播放器

    前言 我觉得每一段自己努力的时光都需要被自己记录下来,所以就有了想法记录我这一个月看视频的所得.在这个过程中自己没有像之前一样,遇到自己解决不了的问题就逃避.虽然说解决一个问题的速度还是有点慢,大概是 ...

  4. Vue音乐播放器(1)

    1.vue.config.js 可以参考: https://juejin.im/post/5bd02f98e51d457a944b634f https://segmentfault.com/q/101 ...

  5. DIY一个自己的音乐播放器

    前言:在最近的一个外包项目中包联盟(PC端)中使用到了video,遇到了好多坑.突发奇想来踩一踩audio的坑?,果然一入深似海,?下面将分享我的DIY之路-Vue音乐播放器. 注:本项目为开源项目, ...

  6. vue引入音乐播放器插件

    欢迎大家进群,一起探讨学习 微信公众号,每天给大家提供技术干货 博主技术笔记 博主网站地址1 博主网站地址2 博主开源微服架构前后端分离技术博客项目源码地址,欢迎各位star vue引入音乐播放器插件 ...

  7. JAVA毕业设计Vue.js音乐播放器设计与实现计算机源码+lw文档+系统+调试部署+数据库

    JAVA毕业设计Vue.js音乐播放器设计与实现计算机源码+lw文档+系统+调试部署+数据库 JAVA毕业设计Vue.js音乐播放器设计与实现计算机源码+lw文档+系统+调试部署+数据库 本源码技术栈 ...

  8. Vue实现仿音乐播放器项目总述以及阶段目录

    Github地址 https://github.com/badaoliumang/vuemusicplayer Vue实现仿音乐播放器各阶段代码 https://download.csdn.net/d ...

  9. Vue实现仿音乐播放器6-实现新歌速递与swiper轮播图切换

    前言 前面在首页已经完成今日推荐以及访问百度API获取数据,现在继续来完善home主页. 效果 新歌速递 swiper实现轮播图 实现 实现新歌速递 在components下新建新歌速递组件News_ ...

  10. Vue实现仿音乐播放器3-将项目托管到git以及github

    Github新建项目 1.登录github,点击右上角新建仓库 2.输入仓库名以及描述等,点击Create resposity 3.新建仓库完成后,右边有个clone or download,复制SS ...

最新文章

  1. jquery获得option的值和对option进行操作
  2. 使用Session服务未开启错误解决方案
  3. linux 共享内存_盘点那些linux 后台开发类常见问题及知识点
  4. mysql 唯一索引出现重复数据_MySQL 创建唯一索引忽略对已经重复数据的检查
  5. 小明爱跑步-扩展-多个对象属性之间互不干扰
  6. 小程序 图片居中显示
  7. 高性能计算机的基准测试程序包括,QX∕T 148-2020 气象领域高性能计算机系统测试与评估规范(可复制版)(40页)-原创力文档...
  8. java 选择图片 显示不出来的_JAVA 窗体选择图片显示在窗体中
  9. 动态规划之LIS(最长上升子序列)
  10. jQuery和asp.net mvc相关资源链接
  11. 用计算机怎样弄出告白密码,数字表白密码 表白密码大全
  12. MSDOS(MBR)、GPT、BIOS、UEFI
  13. 东华大学2020考研计算机OJ题目解答分享——进阶篇(34)
  14. 如果你狂按F2,F12,DEL也进不了BIOS怎么办?
  15. AOE网与关键路径、关键路径算法
  16. 赵小楼《天道》深度解析(74)站着对话、品性、尊严都需要代价和成本的
  17. 我薅了四年的国内外免费服务器
  18. 简单聊聊消息队列的事务补偿机制
  19. live2d手机制作软件_live2dviewerex手机版app
  20. RH358管理打印机和打印文件--配置和管理打印机

热门文章

  1. Android 第三方登录之支付宝登录
  2. 数据分析各省高考难度,河南两广山西 最难
  3. arm开发板与PC通讯及访问外网
  4. 赴日java常问问题_赴日软件工程师java笔试题
  5. 6个免费、免版权视频素材网站
  6. 关于:使用 OCT 自定义部署 Office 2007-2016
  7. 最新版腾讯防水墙(二代)识别
  8. 3. 用户/管理员注册登录 - 如何使用个人Facebook来登录门户网站
  9. 【基站位置查询】通过lac,cellid进行手机基站位置查询和经纬度查询
  10. 信息安全快讯丨叶落知秋,e讯知安全