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

需求:封装一个音乐播放器组件,并在同一个页面多次引入,播放其中一个时其它暂停播放,效果图如下:
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>

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容