遇到需要播放音频的需求,我们不一定要使用audio标签,还可以用音频API AudioContext来帮助我们完成。
前一段在做世界杯超级直播间时有送礼物的需求,例如下图(为了显示,这里调成了无限播放):
只有动画不够,我们需要有声音。
没有用audio,一是不想多插入一个DOM节点,二是想跟随系统的状态,即手机调成震动/静音模式了,这个声音也就不要出了。这个时候AudioContext就可以帮上忙了,代码如下。
let AudioContext = window.AudioContext || window.webkitAudioContext;
let audioCtx = AudioContext ? new AudioContext() : '';
let soundBuffer = {
getBuffer(link) {
return new Promise((resolve, reject) => {
if (audioCtx) {
let request = new XMLHttpRequest();
request.open("GET", link, true);
request.responseType = "arraybuffer";
request.onload = function() {
audioCtx.decodeAudioData(request.response, function(buffer) {
resolve(buffer)
}, function(e) {
console.log('reject');
reject(e);
});
};
request.send();
} else {
reject('not support AudioContext');
}
})
},
createSound(buffer) {
if (audioCtx.state != 'running') {
console.log('重启audioCtx');
audioCtx.resume();
}
let analyser = audioCtx.createAnalyser();
let gainNode = audioCtx.createGain();
let source = audioCtx.createBufferSource();
source.buffer = buffer;
source.connect(analyser);
analyser.connect(gainNode);
gainNode.connect(audioCtx.destination);
return source;
}
}
export default soundBuffer;
上述的createSound
方法其实没必要那么复杂,因为这里其实只是简单的加载了一个线上音频,并没有用js来生成音频,所以增益器、分析器什么之类的可以不使用,简化版如下:
createSound(buffer) {
if (audioCtx.state == 'suspended') {
console.log('重启audioCtx');
audioCtx.resume();
}
let source = audioCtx.createBufferSource();
source.buffer = buffer;
source.connect(audioCtx.destination);
return source;
}
注意createSound
方法第一行的检测,AudioContext是有状态的
在使用中我们发现,ios手机,经常会切home键回来后,音频不播放了,查了一下文档发现
播放时进入running
状态
播放完成切home键会进入interrupt
状态
再回到原网页,很大概率会进入到suspended
状态
这时候,无论怎么调用start 或者干脆new AudioContext()
后重新调用,音频都不会再播放
AudioContext也提供了方法监听他的状态API文档 ,可以监听到挂起后就重置,但是不建议这么做,毕竟系统自动挂起是为了节约性能,我们按需重启即可。
使用上,可以在页面load后preload音频
import soundBuffer from './soundBuffer';
// ...
// preload
soundBuffer
.getBuffer(item.sound) // item.sound是音频线上地址
.then(buf => {
this.soundBuf[item.name] = buf; // 存储,方便调用
})
.catch(e => {
console.log(e);
});
// ...
// 使用
let sound;
if (this.soundBuf[item.name]) {
sound = soundBuffer.createSound(this.soundBuf[item.name]);
sound.start(0); // 播放
} else {
console.log("download sound failed?");
}
除了很老旧的机型之外,这个方法播放都是没有问题的,放心使用。
相关链接:
AudioContext MDN
一个音频库Tone,还没看实现github