專案背景
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風格大致如下:
需要支援的功能:
- 顯示音訊資源的相關資訊,顯示音訊標題,音訊時長等
- 音訊播放,暫停
- 音訊進度點選拖動
- 音訊迴圈播放
- 載入、出錯、結束等狀態識別
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()方法來重新例項化