缘由:在调用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)
})
复制代码