-
Cannot read property `catch` of undefined
原因:在呼叫play()時,現代瀏覽器返回的是一個promise,對於執行失敗的,會觸發一個Unhandled Promise Rejection,但是對於低版本的瀏覽器,呼叫play()並不會返回一個promise。
解決:應該在呼叫play()時做如下處理,增加對playPromise的判斷
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. }); } 複製程式碼
-
InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable
原因:對於還沒有設定src的audio,就直接設定currentTime是會觸發一個INVALID_STATE_ERR異常的。即使是設定currentTime = 0也會觸發這個異常
解決:在設定currentTime之前,必須先設定audio的src
參考資料:Offsets into the media resource
media .
currentTime
[ = value ]Returns the current playback position, in seconds.
Can be set, to seek to the given time.
Will throw an
INVALID_STATE_ERR
exception if there is no selected media resource. Will throw anINDEX_SIZE_ERR
exception if the given time is not within the ranges to which the user agent can seek. -
NotAllowedError
原因:在呼叫play()時可能會觸發一個NotAllowedError的reject,原因是因為瀏覽器在某些情況下播放失敗,常見場景是,未通過點選的情況下呼叫play() ,或者點選事件的回撥中是在下一個tick裡呼叫的play,例如在setTimeout裡呼叫的play,再或者新建立了很多個audio元素,但是並不是每個audio都是通過使用者點選來呼叫的play()等等。
-
場景一:未通過點選等事件繫結,直接呼叫play(),觸發NotAllowedError。解決方法,把呼叫play()的部分放在事件回撥裡,如下程式碼:
playButton.addEventListener("click", () => { audioElem.play() }, false); 複製程式碼
-
場景二:在點選事件回撥中的下一個tick裡呼叫play(),這種情況的示例程式碼如下,
// 錯誤程式碼示例 playButton.addEventListener("click", () => { setTimeout(() => { audioElem.play() }, 100) }, false); 複製程式碼
這種情況,某些版本【在iOS12.0.1親測有坑】也會觸發NotAllowedError異常,應該避免這種情況,可以考慮如下hack手段解決
// hack playButton.addEventListener("click", () => { audioElem.muted = true let p = audioElem.play() if (p !== undefined) { p.then(() => { audioElem.muted = false audioElem.pause() setTimeout(() => { audioElem.play() }, 100) }).catch((e) => { console.log(e) }) } }, false); 複製程式碼
-
場景三:建立了多個audio元素,但是並不是每個audio都是通過使用者點選來呼叫的play()的,這時候某些版本【在iOS12.0.1親測有坑】也會觸發NotAllowedError異常。對於這種情況,最好的辦法就是隻建立一個audio元素,後面通過改變src來播放不同的音樂資源。只要audio通過了事件回撥裡呼叫過play,後續都可以直接呼叫play了,而無需再次繫結事件回撥裡去執行,並且這樣也可以避免建立多個audio來減少記憶體使用。
playButton.addEventListener("click", () => { audioElem.src = "https://a.mp3" audioElem.play() }, false); // 後面其他地方,可以改變src來直接play audioElem.src = "https://b.mp3" audioElem.play() 複製程式碼
-
-
iOS 中頁面隱藏和顯示時,播放audio行為異常
原因:在某些iOS版本中【iOS12.0.1親測有坑】,當我們監聽頁面隱藏和顯示事件,在隱藏時呼叫pause() 暫停,顯示時呼叫play()恢復播放。當按下home鍵,頁面進入系統後臺時,pause()正常呼叫,audio被正常暫停,但是但再次進入頁面,顯示事件中呼叫play()就會出現異常了,
-
第一種異常,如果,我們只是單純的呼叫
audioElem.play()
,不會丟擲任何錯誤,但是audio實際卻沒有真正播放,無任何聲音; -
第二種異常,如果我們每次在顯示事件中執行如下程式碼中任意一種場景,都會在很多情況下會丟擲一個AbortError異常,極少數情況才會正常播放。
//監聽頁面顯示隱藏事件 addPageVisibilityListener(() => { // 隱藏時暫停 audioElem.pause() },() => { // 顯示時恢復播放 // 重新直接賦值src audioElem.src = "https://b.mp3" audioElem.play() // 或者load // audioElem.load() // audioElem.play() }) 複製程式碼
解決:這兩種異常行為應該都是iOS 12.0.1系統本身的bug。我們可以通過如下2中方式來避免這種兩種異常的發生,
- 方式1, 顯示load(),並監聽canplaythrough,推薦使用這種方式
const playAudio = () => { audioElem1.removeEventListener(`canplaythrough`, playAudio) let p = audioElem.play() if (p !== undefined) { p.catch((e) => { console.log(e) }) } } //監聽頁面顯示隱藏事件 addPageVisibilityListener(() => { // 隱藏時暫停 audioElem.pause() },() => { // 顯示時恢復播放 audioElem.load() audioElem.addEventListener(`canplaythrough`, playAudio) }) 複製程式碼
- 方式2,通過setTimeout以及retry來hack避免這種異常發生
let playAudio = (retry: boolean) => { let p = audioElem.play(); if (p !== undefined) { p.catch((e) => { if (retry) { setTimeout(() => { playAudio(false); }, 0); } }); } } //監聽頁面顯示隱藏事件 addPageVisibilityListener(() => { // 隱藏時暫停 audioElem.pause() },() => { // 顯示時恢復播放 setTimeout(() => { playAudio(true) }, 500) }) 複製程式碼
-
HTML5中Audio使用踩坑彙總
相關文章
- Webpack的踩坑與彙總Web
- golang的defer踩坑彙總Golang
- windows下Hive搭建踩坑彙總WindowsHive
- HTML5中audio標籤的使用HTML
- VUE 使用中踩過的坑Vue
- HTML5 錄音的踩坑之旅HTML
- 使用setInterval與clearInterval踩的小坑總結
- MQTT使用踩坑MQQT
- Vue + TypeScript 踩坑總結VueTypeScript
- [踩坑] Go Modules 使用Go
- URLEncoder使用踩坑
- Audio使用總結
- c語言踩坑總結C語言
- 使用ABP框架中踩過的坑系列2框架
- 【Unity】XLua巨坑彙總Unity
- vue-element-admin 使用過程中踩坑Vue
- Kafka SASL ACL配置踩坑總結Kafka
- JasperReport 中踩過的坑
- BootStrap中模態框踩坑boot
- vue中使用protobuf踩坑記Vue
- vue+iframe使用及踩坑Vue
- stylelint 接入實戰踩坑總結
- html5 audio整理HTML
- HTML5 移動端自適應方案與踩坑HTML
- HTML5 Audio & Video - 相容性總結(一)HTMLIDE
- 又踩坑了!BigDecimal使用的5個坑!Decimal
- Redis中的Scan命令踩坑記Redis
- mpvue小程式以及微信直播踩坑總結Vue
- ReactNative 之FlatList踩坑封裝總結React封裝
- [心得]Mac下編譯thrift踩坑總結Mac編譯
- 使用 Typescript 踩 react-redux 的坑TypeScriptReactRedux
- SpringBootAdmin-使用踩坑Spring Boot
- 使用BeanUtils.copyProperties踩坑經歷Bean
- Flutter之Dialog使用和踩坑Flutter
- 那些年使用Hive踩過的坑Hive
- angular踩坑Angular
- 相容踩坑
- Flutter 踩坑Flutter