需求:封裝一個音樂播放器組件,并在同一個頁面多次引入,播放其中一個時其它暫停播放,效果圖如下:

1
1.播放器的代碼可以直接參考Vue 實現(xiàn)音樂播放器,上圖樣式是稍微改了下的,看的更順眼點。
2.為了解決點擊一個播放其它暫停(指的是按鈕變回暫停),目前做法是通過父頁面的數(shù)據(jù)設(shè)置一個isPlay進行控制,當組件播放狀態(tài)進行變化時,修改數(shù)據(jù)的isPlay達到修改組件按鈕狀態(tài)的目的。
3.遇到的另一個問題是,滑塊拖動進度條問題,滑塊拖動成功,但是進度設(shè)置無效。解決方法可以看這里vue實現(xiàn)audio進度拖拽播放及拖拽播放問題解決
原因:

2
解決方式:

3
對應(yīng)本文的代碼就是:

4

5
完整代碼
父頁面引用
<template>
<div style="margin-top: 10px" v-for="(item, index) in list" :key="index">
音樂{{ index }}
<audioPlayer
:audioSrc="item.src"
:id="index"
:isPlay="item.isPlay"
@changSatus="changSatus"
/>
</div>
</template>
<script>
import audioPlayer from '@/components/audioPlayer/index'
export default {
components: {
audioPlayer,
},
data() {
return {
list: [
{
src: 'xxx', // 音頻路徑
isPlay: false, // 播放狀態(tài)
},
{
src: 'xxx',
isPlay: false,
},
{
src: 'xxx',
isPlay: false,
},
],
}
},
methods: {
// 子組件傳回當前點擊的播放器狀態(tài),其它音頻的播放狀態(tài)全部設(shè)置為暫停
changSatus(index, status) {
this.list.forEach((item, i) => {
if (i === index) {
this.$set(this.list[index], 'isPlay', status)
} else {
this.$set(this.list[i], 'isPlay', false)
}
})
},
},
}
</script>
組件頁
<template>
<div class="audio-player-box">
<el-row :gutter="10">
<el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="3">
<div class="play-btn-box">
<i
class="play-btn"
:class="isPlay ? 'el-icon-video-pause' : 'el-icon-video-play'"
@click="musicPlay()"
></i>
</div>
</el-col>
<el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="16">
<div
class="slider-box"
@mousedown="isDraging = true"
@mouseup="isDraging = false"
>
<el-slider
v-model="sliderVal"
:format-tooltip="formatTooltip"
:min="sliderMin"
:max="sliderMax"
@change="spliderSelect"
/>
</div>
</el-col>
<el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="5">
{{ currentTime }} / {{ duration }}
</el-col>
</el-row>
<audio
:ref="`singeBox_${id}`"
class="audio"
controls
:src="src"
style="display: none"
>
<source src="horse.mp3" type="audio/mpeg" />
您的瀏覽器不支持 audio 元素。
</audio>
</div>
</template>
<script>
export default {
name: 'AudioPlayer',
props: {
audioSrc: {
// 音頻路徑
type: String,
default: '',
},
id: {
// 標識
type: Number,
default: 1,
},
isPlay: {
// 是否正在播放
type: Boolean,
default: false,
},
},
watch: {
isPlay(newV, oldV) {
this.play = newV ? true : false
},
},
data() {
return {
box: {}, // audio對象
src: '', // 播放地址
duration: '00:00', // 音樂總時長
currentTime: '00:00', // 當前播放時長
sliderVal: 0, // 這個對接當前時長。
sliderMin: 0,
sliderMax: 0, // 這個對接總時長。
index: 0, // 當前播放的音樂素質(zhì)索引
play: false, // 播放狀態(tài),true為正在播放
isDraging: false, // 設(shè)置拖動修改的時機
}
},
mounted() {
const _this = this
this.src = this.audioSrc
this.$nextTick(() => {
_this.init()
})
},
methods: {
init() {
const _that = this
this.box = this.$refs['singeBox_' + this.id]
// 綁定三個觸發(fā)方法
// 當時長有變化時觸發(fā),由"NaN"變?yōu)閷嶋H時長也算
this.box.ondurationchange = function () {
console.log('時長發(fā)生了變化')
_that.updateTime()
}
// 當前數(shù)據(jù)可用是觸發(fā)
this.box.oncanplay = function () {
console.log('已經(jīng)可以播放了')
}
// 播放位置發(fā)送改變時觸發(fā)。
this.box.ontimeupdate = function () {
console.log('播放位置發(fā)送了變動')
_that.updateTime()
}
// 音頻播放完畢
this.box.onended = function () {
// console.log('播放完畢,謝謝收聽')
}
// 音頻播放完畢
this.box.onerror = function () {
// console.log('加載出錯!')
}
},
updateTime() {
if (!this.isDraging) {
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.sliderMax = this.box.duration
// 值為xx.xxxxx 需要取整
this.sliderVal = Math.floor(this.box.currentTime)
}
},
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'),
}
},
formatTooltip(val) {
// 格式化毫秒數(shù),由于組件提示為純數(shù)字,所以這里需要將數(shù)字轉(zhuǎn)化為我們所需要的的格式,string類型的。'03:45',
const time = this.formatTime(val)
return `${time.min}:${time.sec}`
},
spliderSelect() {
// 滑塊松動后觸發(fā)。更新當前時長,
// 時長發(fā)生變動會init里的方法進行更新數(shù)據(jù)
this.box.currentTime = this.sliderVal
},
musicPlay() {
this.play = !this.play
if (this.play) {
const audios = document.getElementsByTagName('audio')
;[].forEach.call(audios, function (i, index) {
if (i !== audioDom) {
i.pause()
// i.currentTime = 0
}
})
let audioDom = this.$refs['singeBox_' + this.id]
audioDom.play()
this.$emit('changSatus', this.id, true)
} else {
this.box.pause()
this.$emit('changSatus', this.id, false)
}
},
},
}
</script>
<style lang="scss" scope>
.audio-player-box {
width: 500px;
height: 50px;
line-height: 50px;
border: 1px solid #ccc;
.play-btn-box {
text-align: center;
.play-btn {
display: inline-block;
width: 20px;
height: 20px;
font-size: 30px;
cursor: pointer;
color: #409eff;
line-height: 50px;
}
}
.slider-box {
position: relative;
top: 6px;
}
}
</style>
如果有更好的實現(xiàn)方式,歡迎留言探討。