好久没有听音乐了,最近想去听,发现很多歌曾经喜欢因为版权变灰色了。于是萌生了自己写个播放器项目。让听歌不再担忧那天不能再听了。
以下是实现思路和过程。欢迎探讨~
需求
1、音乐播放暂停
2、音乐切换
3、音乐拖动跳段播放
实现
1、播放器内容初始化
#页面加入audio标签
# js部分
# data
box: undefined, # audio对象
musicPath: '******', // mp3链接
coverPath: '******', // 封面
musicTitle: '', // 当前播放标题
musicImg: '', // 当前播放封面
list: [ //数据格式
{
id: 1,
name: '活该',
url: 'HuoGai',
cover: 'ChenYou',
singer: '谌宥',
time: '04:12'
}...
]
# methods方法区加入初始化方法
init() {
this.box = this.$refs.singeBox
this.box.src = this.list[0].url # 歌曲链接
// 绑定三个触发方法
// 当时长有变化时触发,由"NaN"变为实际时长也算
this.box.ondurationchange = function() {
console.log('时长发生了变化')
}
// 当前数据可用是触发
this.box.oncanplay = function() {
console.log('已经可以播放了')
}
// 播放位置发送改变时触发。
this.box.ontimeupdate = function() {
console.log('播放位置发送了变动')
}
// 音频播放完毕
this.box.onended = function() {
console.log('播放完毕,谢谢收听')
}
// 音频播放完毕
this.box.onerror = function() {
console.log('加载出错!')
}
}
// mounted 页面进入是完成初始化
mounted() {
this.init()
},
初始化结果
2、对接audio属性
controls 属性让audio控件进行了展示。但是由于外观和内容不是我们能随心进行控制的。所以我们需要独立去一个假的播放组件,去对接audio 的相关属性。
首先,对接时间
# 页面加入
总时长:{{ duration }}
当前时长:{{ currentTime }}
# data 加入
duration: undefined, # 音乐总时长
currentTime: undefined, # 当前播放时长
# method
updateTime() { // 更新时间
const total = this.formatTime(this.box.duration)
const current = this.formatTime(this.box.currentTime)
this.duration = `${total.min}:${total.sec}`
this.currentTime = `${current.min}:${current.sec}`
this.musicTitle = this.list[this.index].name + ' - ' + this.list[this.index].singer
this.musicImg = this.coverPath + this.list[this.index].cover + '.jpg'
},
formatTime(time) { // 格式化毫秒,返回String型分秒对象
// 有可能没获取到,为NaN
if (!time) return { min: '00', sec: '00' }
return {
min: Math.floor(time / 60).toString().padStart(2, '0'),
sec: Math.floor(time % 60).toString().padStart(2, '0')
}
},
初始化init()里的变动方法,加入更新方法
init() {
this.box = this.$refs.singeBox
this.box.src = this.list[0].url # 歌曲链接
// 绑定三个触发方法
// 当时长有变化时触发,由"NaN"变为实际时长也算
this.box.ondurationchange = function() {
console.log('时长发生了变化')
_that.updateTime()
}
// 当前数据可用是触发
this.box.oncanplay = function() {
console.log('已经可以播放了')
}
// 播放位置发送改变时触发。
this.box.ontimeupdate = function() {
console.log('播放位置发送了变动')
_that.updateTime()
}
// 音频播放完毕
this.box.onended = function() {
console.log('播放完毕,谢谢收听')
}
// 音频播放完毕
this.box.onerror = function() {
console.log('加载出错!')
}
},
时间数据对接完成
对接进度条
# 由于比较懒,所以暂时拿现成的进度条组件。
# script 引进 element 滑块组件
import { ElSlider } from 'element-plus'
# components 引入局部组件
components: { ElSlider }
# data 加入
sliderVal: 0, // 这个对接当前时长。
sliderMin: 0,
sliderMax: 0, // 这个对接总时长。
# 页面加入
# method
formatTooltip(val) {
// 格式化毫秒数,由于组件提示为纯数字,所以这里需要将数字转化为我们所需要的的格式,string类型的。'03:45',
const time = this.formatTime(val)
return `${time.min}:${time.sec}`
},
spliderSelect() {
// 滑块松动后触发。更新当前时长,
// 时长发生变动会init里的方法进行更新数据
this.box.currentTime = this.sliderVal
}
这里可以根据自己的组件进行变化。
时间更新加入进度条更新
updateTime() {
const total = this.formatTime(this.box.duration)
const current = this.formatTime(this.box.currentTime)
this.duration = `${total.min}:${total.sec}`
this.currentTime = `${current.min}:${current.sec}`
// 值为xx.xxxxx 需要取整
this.sliderVal = Math.floor(this.box.currentTime)
this.musicTitle = this.list[this.index].name + ' - ' + this.list[this.index].singer
this.musicImg = this.coverPath + this.list[this.index].cover + '.jpg'
},
进度条对接效果
实现播放暂停,上一首,下一首
# 页面加入
当前歌曲:{{ list[index].name }}
# data 加入
index: 0, # 当前播放的音乐素质索引
play: false # 播放状态,true为正在播放
# method
musicPlay(flag) {
switch (flag) {
case 'pre':
if (this.list[this.index - 1]) {
this.box.src = this.musicPath + this.list[this.index - 1].url + '.mp3'
this.index -= 1
} else {
this.box.src = this.musicPath + this.list[this.list.length - 1].url + '.mp3'
this.index = this.list.length - 1
}
break
case 'play':
this.play = !this.play
break
case 'next':
if (this.list[this.index + 1]) {
this.box.src = this.musicPath + this.list[this.index + 1].url + '.mp3'
this.index += 1
} else {
this.box.src = this.musicPath + this.list[0].url + '.mp3'
this.index = 0
}
break
}
}
到此,简单的播放功能已经实现了。
简单的播放实现
优化+美化
列表播放
去掉audio标签controls属性,不显示自带的控件(功能还在,隐藏了),使用列表的播放方式
# script 引进 element 滑块组件
import { ElTable} from 'element-plus'
# components 引入局部组件
components: { ElTable}
# data
player: {} // 对象控制list谁在播放
# 页面加入表格
{{ row.id }}
{{ row.name }}
{{ row.time }}
{{ row.singer }}
# method 加入点击方法
handlerPlay(id) {
console.log(this.player)
if (!this.player[id]) {
// 没值触发新的播放
const i = this.list.findIndex(x => x.id === id)
this.index = i
this.play = false
}
this.musicPlay('play')
},
# musicPlay方法 重构
musicPlay(flag) {
switch (flag) {
case 'pre':
if (this.list[this.index - 1]) {
this.box.src = this.musicPath + this.list[this.index - 1].url + '.mp3'
this.index -= 1
} else {
this.box.src = this.musicPath + this.list[this.list.length - 1].url + '.mp3'
this.index = this.list.length - 1
}
break
case 'play':
this.play = !this.play
// 对接控件 同步 列表里的控件
if (this.player[this.list[this.index].id]) this.player[this.list[this.index].id].play = this.play
// 新的歌曲播放
if (this.play && !this.player[this.list[this.index].id]) this.box.src = this.musicPath + this.list[this.index].url + '.mp3'
break
case 'next':
if (this.list[this.index + 1]) {
this.box.src = this.musicPath + this.list[this.index + 1].url + '.mp3'
this.index += 1
} else {
this.box.src = this.musicPath + this.list[0].url + '.mp3'
this.index = 0
}
break
}
if (this.play && !this.player[this.list[this.index].id]) {
this.player = {}
this.player[this.list[this.index].id] = {}
this.player[this.list[this.index].id].play = true
} else {
this.play ? this.box.play() : this.box.pause()
}
}
加入对接相关的组件,绑定对应的对接参数。样式这里就忽略了。
image.png
初始化方法init加入连续播放,播放时切个继续播放,暂停时切歌保持暂停判断。
this.box.ondurationchange = function() {
_that.play ? _that.box.play() : _that.box.pause()
_that.updateTime()
}
// 当前数据可用是触发
this.box.oncanplay = function() {
_that.play ? _that.box.play() : _that.box.pause()
}
// 播放位置发送改变时触发。
this.box.ontimeupdate = function() {
_that.updateTime()
}
// 音频播放完毕
this.box.onended = function() {
_that.musicPlay('next')
console.log('播放完毕,谢谢收听')
}
// 音频播放完毕
this.box.onerror = function() {
console.log('加载出错!')
}
实现了最终结果展示:
最终效果