本例子是手機端頁面,該音頻播放是某個音頻點擊后跳轉的頁面,需帶播放音頻的url,若需求不一樣,可以把created中從地址去掉從地址欄讀取url,js代碼邏輯不變,可自行改樣式
示例圖
<template> <div class="pageWrap rowJcAc"> <audio ref="audio" autoplay="autoplay" @ended.stop="audioEnded" :src="audioData.audio_url" controls @loadedmetadata="loadedmetadata" @timeupdate.stop="updateProgress"></audio> <div class="contentBox colAc"> <!-- 中間音頻圖標 --> <img src="../../assets/static/task/music.png" alt="" class="musicImg"> <div class="nameText">音頻文件</div> <div class="progressWrap rowAc"> <div class="startTime">{{audioData.currentTime}}</div> <!-- 進度條區域 --> <div class="progressBarBox"> <div class="progressBox rowAc" @click="clickProgress" ref="progressBox"> <div class="progressBar" :style="{width: 'calc('+percentBar+')'}" ref="progressBar"></div> <!-- <div class="progressBar" :style="{width: progressWidth}"></div> --> <div class="progressCirCle" @click.stop="" @touchmove="progressMove" @touchstart="touchstart"></div> </div> </div> <!-- 結束時間/時長 --> <div class="endTime">{{audioData.duration}}</div> </div> <!-- 播放暫停按鈕圖標 --> <img src="../../assets/static/task/audioStop.svg" v-if="!isPlay" alt="" class="controlImg" @click="playAudio('play')"> <img src="../../assets/static/task/audioPlay.svg" v-else alt="" class="controlImg" @click="playAudio('stop')"> </div> </div> </template> <script> export default { data() { return { audioData: {"audio_name":"20210112_1056.m4a","audio_url":"",duration: '', currentTime: '00:00'}, // content: [ // 存放audio的地址信息,一般是接口返回 // {"audio_name":"20210112_1056.m4a","audio_url":"http://xuke-oss-test-first.oss-cn-shenzhen.aliyuncs.com/TTWY/TTWYXQ000001/homework/audio/79827f5c-8cde-4ccf-add4-7d47e6e20bf9.aac",duration: 0, currentTime: '00:00'}, // {"audio_name":"20210112_1056.m4a","audio_url":"http://xuke-oss-test-first.oss-cn-shenzhen.aliyuncs.com/TTWY/TTWYXQ000001/homework/audio/79827f5c-8cde-4ccf-add4-7d47e6e20bf9.aac",duration: 0, currentTime: '00:00'}, // ] percentBar: '0%', // 進度條百分比 isPlay: true, // 是否是播放狀態 progressBoxWidth: 0, progressWidth: '0px', touchStartX: 0, // 鼠標移動起始位置 touchStartBarWidth: 0, // 滑塊開始時的寬度 } }, created() { // 判斷地址欄跳轉過來有無帶音頻url若有賦值 if(this.$route.params.url) { this.$set(this.audioData,'audio_url',this.$route.params.url) } }, methods: { // 音頻初始化完成獲取秒數 loadedmetadata(event) { this.progressBoxWidth = this.$refs.progressBox.offsetWidth console.log('progressBoxWidth:',this.progressBoxWidth) console.log('錄音時長:',event.target.duration,event) this.$set(this.audioData,'duration',this.transTime(event.target.duration)) this.$forceUpdate() }, // 音頻播放位置發生改變-進度條播放的事件 updateProgress() { let audio = this.$refs.audio // let value = Math.round((Math.floor(audio.currentTime) / Math.floor(audio.duration)) * 100, 0) let value = ((Math.floor(audio.currentTime) / Math.floor(audio.duration)) * 100).toFixed(2) this.$set(this.audioData,'currentTime',this.transTime(audio.currentTime)) this.percentBar = `${value}%` this.$forceUpdate() console.log('音頻進度條改變:',this.percentBar) this.progressWidth = Math.round(Math.floor(audio.currentTime) * this.progressBoxWidth / Math.floor(audio.duration))+'px' }, // 播放結束的處理動作 audioEnded() { console.log('播放結束') this.isPlay = false let audio = this.$refs.audio // audio.currentTime = 0 audio.pause() }, playAudio(type) { console.log('type:',type) let audio = this.$refs.audio if(type == 'play') { this.isPlay = true audio.play() }else { this.isPlay = false audio.pause() } }, // 點擊進度條 clickProgress(event) { this.progressBoxWidth = this.$refs.progressBox.offsetWidth console.log('progressBoxWidth:',this.progressBoxWidth) console.log('點擊進度條:',event) var duration = this.$refs.audio.duration var currentTime = Math.floor(event.offsetX * duration / this.progressBoxWidth) this.$refs.audio.currentTime = currentTime console.log(currentTime,'---',this.$refs.audio.currentTime) // this.$set(this.audioData,'currentTime',this.transTime(currentTime)) // this.progressWidth = event.offsetX >= this.progressBoxWidth ? event.offsetX+'px' : this.progressBoxWidth+'px' }, // 滑塊移動 progressMove(event) { console.log('滑塊移動:',event) var duration = this.$refs.audio.duration this.$set(this.audioData,'duration',this.transTime(duration)) var currentTime = Math.floor((this.touchStartBarWidth+event.touches[0].clientX - this.touchStartX) * duration / this.progressBoxWidth) currentTime >= duration ? duration : currentTime this.$refs.audio.currentTime = currentTime // this.$set(this.audioData,'currentTime',this.transTime(currentTime)) }, // 滑塊開始觸摸 touchstart(event) { console.log('滑塊開始觸摸:',event) this.touchStartX = event.touches[0].clientX this.touchStartBarWidth = this.$refs.progressBar.offsetWidth }, // 將秒數轉化成(分:秒)格式 transTime(time) { let duration = parseInt(time) let minute = parseInt(duration / 60).toString().padStart(2, '0') let sec = (duration % 60).toString().padStart(2, '0') return `${minute}:${sec}` }, } } </script> <style lang="scss" scoped> .pageWrap { width: 100%; height: 100%; } .contentBox { width: 100%; .musicImg { margin-top: -0.1rem; width: .82rem; height: .84rem; } .nameText { color: #333843; font-size: .14rem; font-weight: 500; line-height: .21rem; margin-top: .2rem; } .progressWrap { margin-top: .46rem; width: 100%; } .startTime { color: #333843; font-size: .12rem; line-height: .18rem; margin-left: .18rem; margin-right: .16rem; } .endTime { color: #333843; font-size: .12rem; line-height: .18rem; margin-right: .18rem; margin-left: .16rem; } .progressBarBox { position: relative; flex: 1; } .progressBox { position: relative; max-width: 100%; // flex: 1; // height: .2rem; height: 4px; border-radius: 2px; background: #D8D8D8; } .progressBar { // width: calc(30%); max-width: 100% !important; height: 4px; border-radius: 2px; background: #333843; } .progressCirCle { position: relative; width: .2rem; height: .2rem; background: #ffffff; border: 1px solid #E6E6E8; border-radius: 50%; box-sizing: border-box; margin-left: -.1rem; } .controlImg { margin-top: .53rem; width: .28rem; height: .28rem; } } audio { position: absolute; top: 0; display: block; height: 0; width: 0; opacity: 0; } // 橫向排列 .flexRow{ display: flex; flex-direction: row; } // 縱向排列 .flexCol{ display: flex; flex-direction: column; } // 橫向排列 主軸start 555555555555555555 .rowJs{ @extend .flexRow; justify-content: flex-start; } // 橫向排列 主軸between .rowJb{ @extend .flexRow; justify-content: space-between; } // 橫向排列 主軸center .rowJc{ @extend .flexRow; justify-content: center; } // 橫向排列 主軸around .rowJa{ @extend .flexRow; justify-content: space-around; } // 橫向排列 主軸end .rowJe{ @extend .flexRow; justify-content: flex-end; } // 橫向排列 副軸center .rowAc{ @extend .flexRow; align-items: center; } .rowAs{ @extend .flexRow; align-items: flex-start; } .rowAe{ @extend .flexRow; align-items: flex-end; } // 縱向排列 主軸center .colJc{ @extend .flexCol; justify-content: center; } // 縱向排列 副軸center .colAc{ @extend .flexCol; align-items: center; } // 橫向排列 側軸center 主軸center .rowJcAc{ @extend .rowAc; justify-content: center; } // 橫向排列 副軸center 主軸between .rowJbAc{ @extend .rowAc; justify-content: space-between; } .rowJbAe{ @extend .rowJb; align-items: flex-end; } .rowJaAc{ @extend .rowJa; align-items: center; } // 縱向排列 主軸center 副軸center .colJcAc{ @extend .colJc; align-items: center; } .colJcAe{ @extend .colJc; align-items: flex-end; } // 縱向排列 副軸start .colAs{ @extend .flexCol; align-items: flex-start; } // 橫向排列 副軸center 主軸start .rowJsAc{ @extend .rowAc; justify-content: flex-start; } // 橫向排列 主軸end 副軸center .rowJeAc{ @extend .rowJe; align-items: center; } </style>