美文网首页
vue 循环多个audio播放器,点击一个播放其它暂停

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

作者: 夏日清风_期待 | 来源:发表于2022-05-28 22:24 被阅读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>
    

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

    相关文章

      网友评论

          本文标题:vue 循环多个audio播放器,点击一个播放其它暂停

          本文链接:https://www.haomeiwen.com/subject/nkewprtx.html