vue 循环多个audio播放器,点击一个播放其它暂停

2022-05-28  本文已影响0人  清风徐云去
需求:封装一个音乐播放器组件,并在同一个页面多次引入,播放其中一个时其它暂停播放,效果图如下:
1

1.播放器的代码可以直接参考Vue 实现音乐播放器,上图样式是稍微改了下的,看的更顺眼点。
2.为了解决点击一个播放其它暂停(指的是按钮变回暂停),目前做法是通过父页面的数据设置一个isPlay进行控制,当组件播放状态进行变化时,修改数据的isPlay达到修改组件按钮状态的目的。
3.遇到的另一个问题是,滑块拖动进度条问题,滑块拖动成功,但是进度设置无效。解决方法可以看这里vue实现audio进度拖拽播放及拖拽播放问题解决

原因:
2
解决方式:
3
对应本文的代码就是:
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, // 播放状态
          },
          {
            src: 'xxx',
            isPlay: false,
          },
          {
            src: 'xxx',
            isPlay: false,
          },
        ],
      }
    },
    methods: {
      // 子组件传回当前点击的播放器状态,其它音频的播放状态全部设置为暂停
      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, // 当前播放的音乐素质索引
        play: false, // 播放状态,true为正在播放
        isDraging: false, // 设置拖动修改的时机
      }
    },
    mounted() {
      const _this = this
      this.src = this.audioSrc
      this.$nextTick(() => {
        _this.init()
      })
    },
    methods: {
      init() {
        const _that = this
        this.box = this.$refs['singeBox_' + this.id]
        // 绑定三个触发方法
        // 当时长有变化时触发,由"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('加载出错!')
        }
      },
      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) {
        // 格式化毫秒数,由于组件提示为纯数字,所以这里需要将数字转化为我们所需要的的格式,string类型的。'03:45',
        const time = this.formatTime(val)
        return `${time.min}:${time.sec}`
      },
      spliderSelect() {
        // 滑块松动后触发。更新当前时长,
        // 时长发生变动会init里的方法进行更新数据
        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>

如果有更好的实现方式,欢迎留言探讨。

上一篇 下一篇

猜你喜欢

热点阅读