緣由:在調用play()時,現代瀏覽器返回的是一個promise,對於執行失敗的,會觸發一個Unhandled Promise Rejection,可是對於低版本的瀏覽器,調用play()並不會返回一個promise。javascript
解決:應該在調用play()時作以下處理,增長對playPromise的判斷html
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.
});
}
複製代碼
緣由:對於尚未設置src的audio,就直接設置currentTime是會觸發一個INVALID_STATE_ERR異常的。即便是設置currentTime = 0也會觸發這個異常java
解決:在設置currentTime以前,必須先設置audio的srcweb
參考資料:Offsets into the media resourcepromise
media .
currentTime
[ = value ]瀏覽器Returns the current playback position, in seconds.ide
Can be set, to seek to the given time.ui
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.this
緣由:在調用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版本中【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中方式來避免這種兩種異常的發生,
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)
})
複製代碼
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)
})
複製代碼