vue2.x 音訊播放器 使用element ui + Audio實現一款完整的音訊播放器

程式碼星空發表於2018-12-23

專案背景

audio 音訊播放在多媒體開發中非常常見,但是實際專案中你就會發現,每個系統的audio音訊介面都長的不一樣,百花齊放... pw怎麼會同意呢[哈哈],馬上掏出手機,你改成長這個樣子就好了。需要所有的系統顯示統一,只能用庫了。 本著節約成本的原則,上網搜尋了一些,竟然發現支援vue的多媒體元件庫少的可憐,下了一個還不支援vue2[二哈驚訝臉],還是擼起袖子自己寫一個吧。 這個元件是由element ui + audio + iconfont實現,為什麼用了element ,因為不想自己去操心元件風格,而且我們專案的風格也已經定了基調,純粹是順手。iconfont 是用阿里的圖示庫,非常方便,幾乎都可以找到滿足需求的圖示,然後新增到自己的專案,下載包,匯入專案即可。 iconfont 地址: www.iconfont.cn/home/index 關於如何在專案中引入iconfont,可以看這裡: juejin.im/post/5c1ef2… 簡介

編碼

1.引入vue elemnt ui

> import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import '../../assets/icons/iconfont.css';
Vue.use(ElementUI);
new Vue({
  router,
  store,
  render: h => h(App),
}).$mount('#app');
複製程式碼

2.確定音訊ui與功能

ui風格大致如下:

vue2.x 音訊播放器 使用element ui + Audio實現一款完整的音訊播放器

需要支援的功能:

  • 顯示音訊資源的相關資訊,顯示音訊標題,音訊時長等
  • 音訊播放,暫停
  • 音訊進度點選拖動
  • 音訊迴圈播放
  • 載入、出錯、結束等狀態識別

3.主要互動功能實現

1.如何獲取音訊的相關資訊

this.$refs.audioLoad.addEventListener('loadedmetadata', function cb() {
const se = this.duration;
const second = formatSecond(se);
});
function formatSecond(se) {
  let m = parseInt(se / 60, 10);
  m = m < 10 ? `0${m}` : m;
  let s = parseInt(se % 60, 10);
  s = s < 10 ? `0${s}` : s;
  return `${m}:${s}`;
}
複製程式碼

得到的se是未格式化的視訊總時常,格式化後的second即是可以直接顯示的總時常

2.音訊播放的當前進度從哪裡獲取?

定義一個變數標識當前播放時間,通過audio api的timeupdate 事件可以監聽當前音訊播放事件。 得到了當前播放刻度跟總時常,就可以計算當前的播放進度,進度值就可以表示在播放進度條上,定義變數 precent為播放進度

let showPre = '00:00';
this.$refs.aduioObj.addEventListener('timeupdate', () => {
    if (ele.isEnd || !ele.isPlay || parseInt(ele.playPrecent, 10) === 100) return false;
    const palyTime = ele.$refs.aduioObj.currentTime;
    const totalTime = ele.play.second;
    const precent = (Math.floor(palyTime) / Math.floor(totalTime)) * 100;
    ele.playPrecent = precent;
    ele.showPre = formatSecond(palyTime);
    if (parseInt(ele.playPrecent, 10) === 100 && ele.play.loop) {
      ele.$refs.aduioObj.currentTime = 0;
      ele.playPrecent = 0;
    }
  }, false);
複製程式碼

使用者通過滑塊拖動了進度,要如何處理? 通過滑塊的change事件,但是在滑塊這邊,能獲取的值是當前滑塊的佔比,跟上面一個步驟是相反的,我們需要通過precent跟總時長去退出拖動後的當前時間:

@change="precentChange"
precentChange(val) { // 拖動滑塊改變播放進度
  this.isChange = true;
  this.playPrecent = val;
  this.showPre = formatSecond((this.play.second * val) / 100);
  this.$refs.aduioObj.currentTime = (this.play.second * val) / 100;
},
複製程式碼

3.音訊的播放、暫停

好了,到目前為止,我們已經可以輕鬆應對播放進度相關操作了,現在我們來反過來看最左邊的播放按鈕, 點選按鈕、暫停按鈕通過其點選事件,再去手動觸發audio的播放與暫停事件:

audioPlay() {
  this.isPlay = true;
  this.$refs.aduioObj.play();
},
audioPause() {
  this.isPlay = false;
  this.$refs.aduioObj.pause();
},
複製程式碼

4.音訊的迴圈播放

我們知道音訊的迴圈播放是通過設定audio 的loop屬性,而作為一個元件來說,這個屬性是一個從外部可以隨時修改的引數,所以如果在音訊播放過程中,loop屬性發生了改變,那麼意味著其播放狀態要回到開始,播放進度也要歸零,從新開始播放,所以需要重新賦值:

this.$refs.aduioObj.addEventListener('ended', () => {
    ele.$refs.aduioObj.currentTime = 0;
    ele.playPrecent = 0;
    if (!ele.play.loop) {
      ele.isEnd = true;
      ele.isPlay = false;
      ele.showPre = '00:00';
    }
  }, false);
複製程式碼

5.互動優化-音訊載入狀態

音訊檔案比較大,第一次載入沒有那麼快出現,需要做一個loading過渡狀態,避免播放按鈕切換暫停按鈕很久都沒有音訊播放出來 一開始,是在loadedmetadata 事件裡面識別,但是後來某天偶然發現在iphone手機上載入一個有一定大小的音訊的時候,loading消失後還是遲遲不能播放,但是同樣的,其他安卓機卻可以正常播放。原因是ios必須要等資源都完全下載完才可以播放,而安卓卻可以支援斷點續傳。所以去考察了audio的幾個載入鉤子,最後得出終極的解決方案:

 this.$refs.aduioObj.addEventListener('waiting', () => {
    if (this.isLoad) this.isLoad = false;
  });
const u = navigator.userAgent;
  const isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; // android終端
  if (isAndroid) {
    this.$refs.aduioObj.addEventListener('playing', () => {
      if (!this.isLoad) this.isLoad = true;
    });
    this.$refs.aduioObj.addEventListener('canplay', () => {
      this.isLoad = true;
    });
  } else {
    this.$refs.aduioObj.addEventListener('canplaythrough', () => {
      this.isLoad = true;
    });
  }
複製程式碼

watting 鉤子表示的就是音訊資料正在等待,還無法播放,不管是起始,還是拖拽引起的。

模版

到此,音訊播放器的主要互動功能實現已經介紹完了,貼一下ui

<template>
  <div class="audio-upload-item" :class="[isBorder === '1' ?  'isBorder' : '']">
    <span class="audio-title">{{play.title}}</span>
    <div class="paly-control">
  <div class="icons">
    <el-button class="paly-icon paly" v-if="!isPlay || !play.isUplaod"
      :type="!play.isUplaod ? 'info' : 'primary'" icon="iconfont ed-icon-paly"
      circle :disabled="!play.isUplaod" plain @click="audioPlay"></el-button>
      <el-button class="paly-icon" v-if="isPlay && play.isUplaod && isLoad" type="primary"
    icon="iconfont ed-icon-zanting" circle @click="audioPause"></el-button>
    <el-button class="paly-icon" v-if="isPlay && play.isUplaod && !isLoad" type="primary"
    icon="el-icon-loading" circle @click="audioPause"></el-button>
  </div>
  <div class="paly-precent">
    <el-slider v-model="playPrecent" :format-tooltip="formatTooltip"
     :disabled="!play.isUplaod" @change="precentChange"></el-slider>
  </div>
  <div class="play-time">{{showPre + '/'+play.duration}}</div>
</div>
<audio controls="controls" ref="aduioObj" preload="auto" v-show="false"
 :src="play.url" :loop="play.loop">
</audio>
  </div>
</template>
複製程式碼

元件props,可以isBorder是對於音訊播放器ui的設定,而play就是當前音訊的一些基本資訊

props: {
    play: Object,
    isBorder: String,
  },
複製程式碼

概括下audio的屬性與api

audio 屬性

只讀屬性 duration: 媒體時長,數值, 單位s ended: 是否完成播放,布林值 paused: 是否播放暫停,布林值 讀寫屬性:

playbackRate: 播放速度,大多數瀏覽器支援0.5-4, 1表示正常速度,設定該屬性可以修改播放速度
volume:0.0-1.0之間,設定該屬性可以修改聲音大小
muted: 是否靜音, 設定該屬性可以靜音
currentTime:指定播放位置的秒數
複製程式碼

audio 常用api

事件名 何時觸發 loadstart 開始載入 progress 正在載入 suspend 使用者代理有意無法獲取媒體資料,無法獲取整個檔案 abort 主動終端下載資源並不是由於發生錯誤 error 獲取資源時發生錯誤 play 開始播放 pause 播放暫停 loadedmetadata 剛獲取完後設資料 loadeddata 第一次渲染後設資料 waiting 等待中 playing 正在播放 canplay 使用者代理可以恢復播放媒體資料,但是估計如果現在開始播放,則媒體資源不能以當前播放速率直到其結束呈現,而不必停止進一步緩衝內容。 canplaythrough 使用者代理估計,如果現在開始播放,則媒體資源可以以當前播放速率一直呈現到其結束,而不必停止進一步的緩衝。 timeupdate 當前播放位置作為正常播放的一部分而改變,或者以特別有趣的方式,例如不連續地改變。 ended 播放結束 ratechange 媒體播放速度改變 durationchange 媒體時長改變 volumechange 媒體聲音大小改變

其他擴充功能,音訊如何倍數播放?

let myAudio = $('#wdd');
myAudio.playbackRate = 2;
複製程式碼

其他擴充功能,如何隱藏音訊的播放按鈕?

如果你用audio元件顯示

  • 方法1: controlsList="nodownload",這個方法只支援 Chrome 58+, 低於該版本的是沒有無法隱藏的

    controlist,可以取值:

    nodownload: 不要下載 nofullscreen: 不要全屏 noremoteplayback: 不要遠端回放

  • 方法二:通過css隱藏// 這個方式相容所有版本的谷歌瀏覽器 audio::-webkit-media-controls { overflow: hidden !important } audio::-webkit-media-controls-enclosure { width: calc(100% + 32px); margin-left: auto; } 但是,瀏覽器右鍵還是可以選擇下載。。。 所以,還要禁止其右鍵操作: // 給audio標籤禁止右鍵,來禁止下載

  • audio外掛解決小煩惱 這波操作太容易忘記了,給你一個外掛快速解決: 專案地址: github.com/kolber/aud... 優點: 簡單,無依賴 缺點:非同步插入的audio標籤,每次還是需要重新呼叫audiojs.createAll()方法來重新例項化

相關文章