🎵前言
今天在公司接到一个需求。需求如下,首先前端发起一个异步请求,接着后端会通过socket发送一个携带音乐url的消息到前端, 前端拿到url后需要自动播放这个音乐。众所周知,因为流量,安全等因素的限制,在现代浏览器中,尤其是移动端的浏览器中,是不允许在没有用户交互的情况下自动播放音乐的。在业务中获取音乐url的socket消息,会触发的比较频繁,显然不太可能在每一次接受消息后,弹一个模态框让用户确认播放音乐。难道,我们真的没有办法解决这个问题吗?其实是有的。(因为目前光顾着实现功能,还没有来得及探究背后的原理,还请见谅🙊)
⚠️ps: 此办法只适用于发生过一次用户交互行为的情况下,比如在上述的需求中,前端发送异步请求需要点击一个按钮
💡思路
其实一开始是,我是没有任何思路的。无论是stackoverflow,还是思否,掘金搜了一圈都没有好的解决方法。但是在无意间,我发现了一个项目拥有类似的功能(异步获取音频url后,自动播放音频)。于是看了一下项目的源码,发现了其中的奥妙所在😂
✨这一切的实现,主要依赖了这个包StartAudioContext
On iOS, the Web Audio API requires sounds to be triggered from an explicit user action, such as a tap. Calling noteOn() from an onload event will not play sound. StartAudioContext listens for the first non-dragged touchend or mouseup event on any of the given elements, then triggers a silent AudioBuffer which will start the AudioContext if it isn't already started.
StartAudioContext会监听任意的元素(没有指定参数时将会监听document.body)的除了ouchend或mouseup以外的任意事件。触发事件后,会触发一个静音的AudioBuffer。(在IOS中实现音频自动播放,需要依赖这个AudioBuffer)
💻代码
这里肯定不是公司的源码,因为涉及到保密原则。但是对于理解如何实现这个功能,还是足够的。
import StartAudioContext from 'startaudiocontext'
let audioCtx
window.onload = function () {
audioCtx = new AudioContext || webkitAudioContext()
// 移动端需要在页面初始化时,就需要创建
StartAudioContext(audioCtx)
}
function autoPlay (url) {
let source = audioCtx.createBufferSource()
let request = new XMLHttpRequest()
request.open('GET', url, true)
request.responseType = 'arraybuffer'
request.onreadystatechange = () => {
if (request.readyState !== 4) {
return
}
audioCtx.decodeAudioData(
request.response,
(buffer) => {
source.buffer = buffer
source.connect(audioCtx.destination)
source.loop = false
// 开始播放
source.start(0)
})
}
}
request.send()
}
button.onclick = function () {
// 我需要音乐
}
// 接受消息
socket('msg', ({ url }) => {
// 自动播放
autoPlay(url)
})