先來個效果圖韻下味:
需求:
- 音頻的播放,暫停,中間按鈕狀態的變化,播放時實時更新播放進度;
- 前進15s,后退15s;
- 進度條拖動。
一開始想着這3個功能應該挺簡單的。不就是播放,暫停,前進,后退么~呵~寫的時候發現自己還是太年輕。當然,這跟自己的技術功底有關系。現在把我遇到的難點及要注意的點說一下~
- 需要設置一個名為seekPosition的全局變量,初始值為0。我們要在播放的時候實時記錄播放的位置,存到該變量里,這是方便在前進15s 或 后退15s 時計算時間點。
- 前進15s時要判斷剩余時間是否>15s,如果<15s,則返回到開始位置;
- 后退15s時要判斷播放時間是否>15s,如果<15s,則返回到開始位置;
- 點擊播放時,要先判斷seekPisition是否>0,如果>0,則跳轉到seekPosition的位置,並播放;
- 拖動時,要先讓音頻停止播放,拖動結束后,再播放音頻。並且要計算拖動位置對應的時間。
完整代碼:
wxml:
<view class=" audio-content"> <image src="{{logo640x360}}" class="bg-blur"></image> <view class="bg-gray"></view> <view class="container-cover"> <view class="cover"> <image src="{{logo640x360}}" class="cover-image"></image> <view class="cover-tip">詞不達意.mp3</view> </view> </view> <view class="audio-inner row item-center"> <view class="audio-desc">{{currentProcess}}</view> <view class="audio-progress-wrap"> <van-slider value="{{sliderValue}}" step="{{sliderStep}}" active-color="#09bb07" use-button-slot bind:change="onSliderChange" bind:drag="onSliderDrag"> <view class="slider-button" slot="button"></view> </van-slider> </view> <view class="audio-desc">{{productDetail.duration}}</view> </view> <!-- 播放器控制條 --> <section class="speech-player js_control_bar"> <view class="player-bar row justify-center item-center"> <view class="backward js_audio_backward" title="后退15s"> <ss-icon name="back-15" size="32px" color="#fff" block="{{true}}" bind:click="backward" /> </view> <view class="play" title="播放/暫停"> <view class="circle-loading" wx:if="{{loading}}"></view> <ss-icon name="play-outline" size="50px" color="#fff" block="{{true}}" wx:if="{{pause}}" bind:click="audioPlay" /> <ss-icon name="pause-outline" size="50px" color="#fff" block="{{true}}" wx:if="{{playing}}" bind:click="audioPause" /> </view> <view class="forward js_audio_forward" title="前進15s"> <ss-icon name="forward-15" size="32px" color="#fff" block="{{true}}" bind:click="forward" /> </view> </view> </section> </view>
這里面用到了有贊的小程序組件 icon組件 和 slider 組件。如果你要用的話把上面的 <ss-icon> 換成<van-icon>,並且需要自己找阿里矢量圖標庫查找對應的圖標。
js:
// components/product/audio/index.js let audioUrl = "", seekPosition = 0; const audioContext = wx.createInnerAudioContext(); Component({ options: { multipleSlots: true, addGlobalClass: true }, /** * 組件的屬性列表 */ properties: { productId: Number, }, /** * 組件的初始數據 */ data: { pause: true, playing: false, loading: false, productDetail: {}, audioDuration: 0, currentProcess: '00:00', sliderStep:1 }, /** * 組件的方法列表 */ methods: { //音頻播放 audioPlay(e) { const _this = this; let audioDuration = _this.data.audioDuration; if (audioUrl) { if (seekPosition) { //如果有指定位置,則跳轉到指定位置 audioContext.seek(seekPosition); } audioContext.play(); _this.setData({ pause: false, playing: true, loading: false, }) } else { //getJSON是我自己封裝的 ss.getJSON('獲取音頻如果需要發送請求,這里面放請求地址', { 放你自己的參數 }, res => { audioUrl = res.t; audioContext.src = audioUrl; if (seekPosition) { audioContext.seek(seekPosition); } audioContext.play(); audioContext.onPlay(() => { console.log('onPlay') }) audioContext.onWaiting(() => { console.log('onWaiting') _this.setData({ pause: false, playing: false, loading: true, }) }) audioContext.onCanplay(() => { console.log('onCanplay') _this.setData({ pause: false, playing: true, loading: false }) setTimeout(() => { audioContext.duration }, 500) _this.audioStatus(); }) audioContext.onError((res) => { console.log(res.errMsg) }) }) } }, //播放暫停 audioPause: function() { const _this = this; audioContext.pause(); _this.setData({ pause: true, playing: false, loading: false }) }, //記錄播放狀態 audioStatus: function() { const _this = this; //音頻播放進度更新事件 audioContext.onTimeUpdate(() => { seekPosition = audioContext.currentTime; _this.setData({ currentProcess: ss.formatSecToMin(audioContext.currentTime), sliderValue: audioContext.currentTime / _this.data.audioDuration * 100, }) }) //音頻播放結束 audioContext.onEnded(() => { seekPosition = 0; _this.setData({ sliderValue: 0, currentProcess: '00:00', playing: false, pause: true }) }) }, //開始拖動 onSliderDrag(e) { const _this = this; if (_this.data.playing) { _this.audioPause() } let sliderValue = e.detail.value; seekPosition = _this.data.audioDuration / 100 * sliderValue; _this.setData({ currentProcess: ss.formatSecToMin(seekPosition) }) }, //拖動結束 onSliderChange(e) { const _this = this; _this.audioPlay() }, //前進15s forward() { const _this = this, audioDuration = _this.data.audioDuration; let currentTime; if (_this.data.playing) { currentTime = audioContext.currentTime; } if (_this.data.pause) { currentTime = seekPosition; } if (audioDuration - currentTime > 15) { seekPosition = currentTime + 15; _this.setData({ sliderValue: seekPosition / audioDuration * 100, currentProcess: ss.formatSecToMin(seekPosition) }); } else { seekPosition = audioDuration; _this.setData({ sliderValue: 0, currentProcess: '00:00' }); } if (audioUrl && _this.data.playing) { audioContext.seek(seekPosition); } }, //后退15s backward() { const _this = this, audioDuration = _this.data.audioDuration; let currentTime; if (_this.data.playing) { currentTime = audioContext.currentTime; } if (_this.data.pause) { currentTime = seekPosition; } if (currentTime > 15) { seekPosition = currentTime - 15; } else { seekPosition = 0; } _this.setData({ sliderValue: seekPosition / audioDuration * 100, currentProcess: ss.formatSecToMin(seekPosition) }); if (audioUrl && _this.data.playing) { audioContext.seek(seekPosition); } } } })
大概就是這些了~有更好解決方案的歡迎留言哈~~