之前项目的时候,遇到一个这样的需求,一是要求一个网页有背景音乐播放,并且切换页面的时候不中断,二是每个按钮点击都需要有音效。乍一看很简单,只需要在页面上创建audio标签,然后在js里调用播放。
但是实际上,当我引入了背景音乐,鼠标音效之后,网页在进来的瞬间直接卡到起飞,直接导致浏览器未响应,就算音频加载完成了,也会导致用户在进行每个操作的时候都会异常卡顿,所以,就必须找到新的方法来解决这个问题。
具体原因
因为在音频标签在引入后,浏览器加载时,会直接同时解析所有的音频,导致浏览器卡顿。所以需要将所有音频预加载,先存储到内存中。
代码案例
1.对于背景音乐,可以使用预加载办法,提前存储到内存中,避免加载和解析导致的卡顿,具体代码如下。
import Vue from 'vue'
//加载背景音乐
const audioUrls = ['/mp3/home-bg.mp3', '/mp3/start-bg.mp3']
const audioMap = {}
for (const url of audioUrls) {
const audio = new Audio()
audio.src = url
audio.load()
audio.onloadeddata = () => {
console.log(`音频 ${url} 加载完成`)
}
audioMap[url] = audio
}
Vue.prototype.$audioMap = audioMap;
在项目的plugins文件夹中创建bgAudio.js,放入上述代码,再进入项目的nuxt.config.js,找到plugins: []部分,加入如下代码:
{ src: '@/plugins/bgAudio.js', mode: 'client' },
调用背景音乐时,代码如下:
this.audio = that.$audioMap['/mp3/start-bg.mp3'];//定义音频对象
this.audio.loop = true; //设置循环播放
this.$audioMap['/mp3/home-bg.mp3'].pause() //暂停播放
this.$audioMap['/mp3/home-bg.mp3'].currentTime = 0 //从0秒开始播放
//其它的控制和一般的audio操作完全一致
这样,由于背景音乐直接js预加载到内存,需要的时候调用,就省略的浏览器加载解析的过程,就解决了一半的卡顿问题。然后,还可以用以下方法判断图片是否加载完成。
var audio1State = this.$audioMap['/mp3/home-bg.mp3'].readyState;
//4就是加载完成,其它的百度查一下吧。
2.对于按钮点击音效,因为按钮操作会非常多,而且可能不同按钮音效不同,所以不能写死去预加载,而是灵活的进行调用。plugins文件夹中创建btnAudio.js,代码如下:
class AudioPlayer {
constructor() {
this.audioContext = null
this.gainNode = null
this.isPlaying = false
this.currentSource = null
this.audioBufferMap = {}
this.initAudio()
}
initAudio() {
//兼容iPhone旧版本
if ('AudioContext' in window || 'webkitAudioContext' in window) {
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
} else {
console.error('Web Audio API not supported');
}
this.gainNode = this.audioContext.createGain()
this.gainNode.connect(this.audioContext.destination)
}
play(src, loop = false) {
if (this.isPlaying) {
this.stop()
}
if (this.audioBufferMap[src]) {
this.playBuffer(this.audioBufferMap[src], loop)
} else {
let request = new XMLHttpRequest()
request.open('GET', src, true)
request.responseType = 'arraybuffer'
request.onload = () => {
this.audioContext.decodeAudioData(request.response, buffer => {
this.audioBufferMap[src] = buffer
this.playBuffer(buffer, loop)
}, () => {
console.error('decodeAudioData error')
})
}
request.send()
}
}
preload(src) {
if (!this.audioBufferMap[src]) {
let request = new XMLHttpRequest()
request.open('GET', src, true)
request.responseType = 'arraybuffer'
request.onload = () => {
this.audioContext.decodeAudioData(request.response, buffer => {
this.audioBufferMap[src] = buffer
}, () => {
console.error('decodeAudioData error')
})
}
request.send()
}
}
playBuffer(buffer, loop) {
this.currentSource = this.audioContext.createBufferSource()
this.currentSource.buffer = buffer
this.currentSource.loop = loop
this.currentSource.connect(this.gainNode)
this.currentSource.start(0)
this.isPlaying = true
}
pause() {
if (this.isPlaying && this.currentSource) {
this.currentSource.stop(0)
this.isPlaying = false
}
}
stop() {
if (this.isPlaying && this.currentSource) {
this.currentSource.stop(0)
this.isPlaying = false
}
}
}
const audioPlayer = new AudioPlayer()
export default ({ app }, inject) => {
inject('audio', audioPlayer)
}
再进入项目的nuxt.config.js,找到plugins: []部分,加入如下代码:
{ src: '@/plugins/btnAudio.js', ssr: false },
调用按钮音效时,代码如下:
this.$audio.play('/mp3/click.wav')
也可以提前预加载,用如下代码:
this.$audio.preload('/mp3/click.wav')
这样,第一次点击按钮时,则加载音乐到存储,之后每次调用同样的文件时直接调用缓存。这样就解决了性能问题,
最后
NuxtJS2+VUE2配合起来,感觉非常适合开发桌面客户端,不过具体还在研究中。对于NuxtJS3,生态支持和文档都太少了,还是过段时间研究把。