Skip to content

vue中实现自定义音频播放器 #22

Description

@phoebeCodeSpace

实现逻辑

  • slider组件 播放进度条、音量进度条
  • timeupdate 监听播放,控制播放进度条
  • play()pause()实现播放与暂停
  • duration 属性获取音频时长
  • currentTime 音频当前播放位置的获取与设置
  • volume 音量的获取与设置

代码

<template>
  <div class="audio">
      <audio :src="audioSrc" ref="audioRecord" ></audio>
      <!-- controls -->
      <div class="audio_controls" >
        <button :class="play ? 'play':'pause'" @click="togglePlay"></button>
        <Slider v-model="time_progress" class="time_progress" @on-change="sliderTime"  :tip-format="formatTimeSlider"></Slider>
        <div class="time_range">
            {{this.formatTime(currentTime)}} / {{this.formatTime(duration)}}
        </div>
        <div class="voice_progress">
            <button :class="sound ? 'lound':'slient'" @click="toggleSound"></button>
            <Slider v-model="voice_progress" @on-change="sliderSound"></Slider>
        </div>
      </div>
  </div>
</template>
<script>
export default {
    name: "audioRecord",
    props:['audioSrc'],
    data(){
        return{
            play:true,
            currentTime:0,
            duration:0,
            time_progress:0,
            sound:true,
            voice_progress:50
        }
    },
    methods:{
        formatTime(val){
            let second = parseInt(val);
            let HH = Math.floor(second / 3600),MM = Math.floor(second % 3600 / 60),SS = second % 60;
            if(HH<10){HH = `0${HH}`};
            if(MM<10){MM = `0${MM}`};
            if(SS<10){SS = `0${SS}`};
            let min = `${HH}:${MM}:${SS}`;
       return min;
        },
        // 播放
        togglePlay(){   // 开始暂停     
           let method = this.play ? 'play' : 'pause';
           this.play = !this.play;
           this.$refs.audioRecord[method]();
        },
        handleProgress(){
            const audioRecord = this.$refs.audioRecord,
                  _currentTime = audioRecord ? audioRecord.currentTime : 0; 
                  if(_currentTime==this.duration){
                     this.play = true;
                  }          
                  this.currentTime = _currentTime;
                  this.time_progress = _currentTime/this.duration * 100;
        },
        sliderTime(val){        // 切换播放时间点
            this.$refs.audioRecord.currentTime = (val/100) * this.duration;
        },
        formatTimeSlider(val){
            return this.formatTime(val*this.duration/100);
        },
        // 音量
        toggleSound(){         // 静音切换
            this.sound = !this.sound;
            if(this.sound){
                this.$refs.audioRecord.volume = this.voice_progress/100;
            }else{
                this.$refs.audioRecord.volume = 0;
            }
        },
        sliderSound(val){      // 音量调节
            this.$refs.audioRecord.volume = val/100;
            val==0 ? this.sound=false : this.sound=true;
        }
    },
    mounted(){
         const audioRecord = this.$refs.audioRecord;
         audioRecord.oncanplay=()=>{ this.duration = audioRecord.duration};             // 监听oncanplay
         this.$refs.audioRecord.addEventListener('timeupdate', this.handleProgress);    // 监听播放
         audioRecord.volume = this.voice_progress/100;
    }
}
</script>
<style scope>
button{
    border: 0;
    outline: 0;
}

.audio{
    width: 100%;
    height: 48px;
    border-radius: 2px;
    background-color: #fafafa; 
}
.audio_controls{
    height: 100%;
    display: -webkit-flex; /* Safari */
    display: flex;
    justify-content: center;
    align-items: center;
    align-content:center;
}
/* 开始/暂停按钮 */
.play,.pause{
    width: 26px;
    height: 26px;
    background-color: #03a9f4;
    background-position: center;
    background-repeat: no-repeat;
    background-image: url(../../assets/images/mp3/play.png);
    border-radius: 50%;
    margin-right: 10px;
}
.play{
    background-image: url(../../assets/images/mp3/play.png);
}
.pause{
    background-image: url(../../assets/images/mp3/pause.png);
}
/* 时间 */
.time_range{
    width: 140px;
    text-align: center;
}
/* 进度条 */
.time_progress{
    width: 300px;
}
.voice_progress .ivu-slider{
    width: 120px;
    display: inline-block;
    vertical-align: middle;
}

/* 音量 */
.voice_progress .ivu-tooltip-rel{
    display:none;
}
.lound,.slient{
    vertical-align: middle;
    width: 26px;
    height: 26px;
    background-position: center;
    background-repeat: no-repeat;
    background-color: #fff;
}
.lound{
    background-image: url(../../assets/images/mp3/openvoice.png);
}
.slient{
    background-image: url(../../assets/images/mp3/closevoice.png);
    background-position: left center;
}
</style>

填坑:

  1. audio.duration的值为NaN
    监听canplay事件

  2. 进度条与currentTime不匹配
    获取的currentTime不需要parseInt,在format事件格式的时候再parseInt

参考:

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions