个人blog 原文连接
最近公司迭代的项目中,新增了音频播放功能,填了很多音频的坑,总结一下:vue
交互需求:ios
固然还有一些隐性需求:git
实现效果以下图所示:(固然,这只是项目用到的一部分,项目中还有其余页面也用到了这个组件,那么就更考验组件的健壮性和可拓展性了。)github
咱们对音频的操做,一般是先获取这个音频 DOM Element,经过对它的操做,实现想要达到的效果,若是你只是设定一个audio
这样单薄的ref名称,恐怕会有些问题,所以我给每一个音频设定了一个惟一的ref名称。chrome
<template> <audio :src="audioSrc" :ref='uniqueId' :data-key="uniqueId" hidden></audio> </template> <script> export default { data () { return { // uniqueId() 是随机生成字符串的方法 uniqueId: uniqueId(), audioSrc: '', } }, computed: { audioElement () { return this.$refs[this.uniqueId] } }, } </script>
注意到上面的代码,我在给 audio 添加属性的时候,多添加了一个 data-key
的属性,那就是为了暂停其余语音而使用的,做为我要操做页面其余音频而设置的标识:segmentfault
// 暂停其余语音的方法 pauseOthers (except) { var audios = document.querySelectorAll('audio') ;[].forEach.call(audios, audio => { if (audio.dataset['key'] !== except.uniqueId) { audio.pause() } }) } // 调用的时候 this.pauseOthers (this)
我想这就是项目坑点之一,由于音频src并非上传语音就返回的,上传语音只返回了语音id,咱们须要经过id再去异步请求一次,才能获取到src。后端
基于这样的前提,播放操做作了两点考虑(单例模式思惟):promise
具体实现:浏览器
togglePlay()
事件判断audioSrc
是否有值bash
audioSrc
,监听音频 canplay
canplay
(这边有一个坑点,后面会提到)为何相关事件的绑定放在 canplay
中? 否则你可能会出现下面的报错:
Uncaught (in promise) DOMException: The element has no supported sources.
因此,答应我,基于audio播放的 事件 或是 属性 ,都放在 canplay
的回调以后。
相关事件包括(本组件中):
timeupdate
: 控制进度条展现pause
: 监听按钮 播放/暂停 样式currentTime
: 控制进度拖动或者中止语音error
: 监听播放错误按钮的样式经过设置一个变量做为状态值,paly()
和 pause()
的时候分别改变状态值。
其它具体逻辑上文描述比较清楚,再也不赘述。
currentTime
设置为0currentTime
值,并设置currentTime
两个操做都涉及到了currentTime
的设置,咱们在这里遇到了两个坑:
设置currentTime继续播放
一开始仍然觉得是后端的锅,由于当我静态设置一个 audioSrc 的时候,是没有问题的,可是当我动态设置,就会出现这样的问题:不管我是播放状态仍是暂停状态,设置到相对应的currentTime
都会继续播放。
经过排查,发现当我设置currentTime
会再次触发一次 canplay
事件, canplay
的回调是绑定播放的相关操做,所以会继续播放。
解决办法,温习了一遍addEventListener的语法,绑定canplay
事件最多只调用一次。
this.audioElement.addEventListener('canplay', () => { // ...相关操做 }, { once: true })
音频的打断包括两种状况:
destroyed
第一种状况,解绑相关事件,释放内存。
第二种状况,具体描述一下:
当用户从新上传新的语音,不论此时语音暂停仍是播放状态,都应该中止。
咱们经过 watch
监听 id (上传返回来的音频id),当id变化的时候,将 audioSrc
清空,以避免播放旧的音频内容。
然而,仅仅这样是不够的,若是监听 error
事件,就会发现报错,解决的办法仍是解绑相关事件,即,咱们在 canplay
回调中的绑定的相关事件,让audio恢复初始状态,等到下一次播放的时候,须要从新请求新的src,回到上面播放的部分。
在解决问题的过程当中,也查询到了一些实用的知识点,虽然在应用中没有运用到,可是这些知识点看起来彷佛挺有用的,为了下次遇到其余坑能快速找到解决办法,先把这些知识点记录下来。
canplay
与 canplaythrough
辨析canplay
事件。canplaythrough
事件。HTMLMediaElement.play()
返回 Promise<video>
或 <audio>
的 play()
返回一个 Promise
,若是播放成功,Promise状态变成fulfilled
,若是播放失败,状态变为rejected
并提供错误信息。
var playPromise = document.querySelector('video').play(); // In browsers that don’t yet support this functionality, // playPromise won’t be defined. if (playPromise !== undefined) { playPromise.then(function() { // Automatic playback started! }).catch(function(error) { // Automatic playback failed. // Show a UI element to let the user manually start playback. }); }
412 通常是由于服务器的 If-Unmodified-Since 或 If-None-Match 未实现
// 解决办法 media.addEventListener('error', function (e) { var date = new Date(); var milliSecs = date.getMilliseconds(); var curr_src = $(media[0]).attr('src'); var curr_src_arr = curr_src.split("?"); var new_src = curr_src_arr[0]+"?t="+milliSecs; $(media[0]).attr('src',new_src); $(media[0]).find('source').attr('src',new_src); media[0].load(); }, false);
暂时完。
后续若是测试妹妹发现了什么bug,我会继续填坑记录滴。