一、需求
- 不使用音频默认的进度条,仅使用UI自定义喇叭icon
- 开启关闭声音可控
- 自动播放
二、【方案一】使用Audio对象
👋1.创建Audio对象
Audio对象的属性,方法,事件等可参考【原文】。
loadAudio(){
var x1 = document.createElement('source');
x1.src="bgm.mp3";
x1.type= "audio/mpeg";
var x2 = document.createElement('source');
x2.src="bgm.ogg";
x2.type="audio/ogg";
y = document.createElement("audio");
y.appendChild(x1);
y.appendChild(x2);//音频格式兼容
y.preload = true;//预加载
y.autoplay = true;//自动播放
y.loop = true;//循环播放
this.isBGM = y.muted;//是否关闭声音
},
其中,创建的Audio对象并没有通过appendChild追加到DOM树中,将不会在页面中展示。
👋2.canplay监听
一般要求,音频资源下载完成,可以播放时开始播放,可监听Audio对象的canplay事件或者使用promise,回调函数等异步策略。
playBGMbyAudio(){
var that = this;
that.loadAudio();//创建Audio对象
y.oncanplay = that.prePlay();//canplay监听
},
👋3.针对Chrome及IOS禁止自动播放的预处理
-
已知:通过设置对象属性
autoplay为true,但Chrome及IOS平台对不符合条件的禁止自动播放,具体可【参考】,这种情况需要用户先交互(click或touch)然后再次执行play()。 -
解决方案:先通过执行
play(),根据页面是否提示错误来区别设备支持与否,做出标记。 -
实践:支持自动播放的,不执行
play的任何回调【报错】;不支持自动播放的,会进入catch回调,在此设置默认声音关闭,做标记等,用户后续通过手动点击开启声音(重新触发play)。prePlay(){ this.enterShow();//其他页面逻辑 // play返回的是一个promise 以此来区分是否可以自动播放 y.play().then(() => { }).catch(err => { // 不支持自动播放 需手动开启声音 后需要重新执行play() this.IOS = true; y.muted = true; this.isBGM = y.muted; }).finally(() => { }); },
👋4.通过设置对象muted属性控制声音开启关闭
controlBGM(value) {
this.IOS&&value&&y.play();//针对标记为IOS的,通过交互事件触发play()
y.muted = !value;
this.isBGM = y.muted;
},
👋5.挂起应用,声音停止
//当app进入后台失活时
y.pause();
//当app被激活时
y.play();
- 只能针对不同app分别监听是否进入后台的状态,实用性不高。
- 不受手机系统是否“静音”控制。
三、【方案二】使用audioContext
📖1.audioContext介绍
参考【audioContext】【Web Audio API】
📖2.封装组件及使用
参考【原文】
📖3.原理
-
XMLHttpRequest和[FileReader](https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader "FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。"):通过XMLHttpRequest请求获取远程音频数据,并指定响应数据类型为arraybuffer,或者使用[FileReader](https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader "FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。")读取本地音频数据。 -
创建
AudioContext对象audio -
audio.decodeAudioData()方法:通过该方法将获得的ArrayBuffer(音轨数据)解码成为AudioBuffer(可被识别播放的数据)。 -
audio.createBufferSource()方法:创建播放对象sound。 -
对播放对象
sound赋值设置谢系列属性:包括解码后的AudioBuffer,loop等。 -
sound.connect(audio.destination)设置播放源:表示音频图形在特定情况下的最终输出地址,通常为扬声器。 -
sound.start(0)开始播放:可定义开始时间。
📖4.优缺点
同样需要用户交互才可以播放音频。
跟随系统状态,受手机系统是否“静音”控制。
四、答疑
💡浏览器是如何播放音频的
在 2016 年,Chrome 官方团队使用“面向服务的架构”(Services Oriented Architecture,简称 SOA)的思想设计了新的 Chrome 架构。也就是说 Chrome 整体架构会朝向现代操作系统所采用的“面向服务的架构” 方向发展,原来的各种模块会被重构成独立的服务(Service),每个服务(Service)都可以在独立的进程中运行。
浏览器任务管理器中的Audio进程,
浏览器对于audio已经实现了单独进程处理,可以明白最开始仅创建了Audio对象并没有追加DOM树,没有走各个页面的“渲染进程”而是通过“Audio Service”来实现功能。
通过Audio对象和audioContextAPI播放音频都是使用的audio进程/服务,但也存在不同。通过Audio对象创建的音频在Chrome浏览器状态栏区域也有音频控制开关,可以更改音频播放状态,而使用audioContextAPI却没有,以此也可以做出区分。
💡如何理解动态创建并未追加DOM树的DOM节点
浏览器会将由DOM树以及render树产生的文档流自上而下自左而右的平铺下来,有些节点由于样式设置会脱离文档流排列但仍存在于DOM树中,通过浏览器“渲染进程”处理。未追加DOM树可能是以变量存在,会被其他进程读取执行。进程间可以通信。
💡如何理解Audio对象可以通过new创建
参考【Audio()】,可能为浏览器接下来的架构调整铺路。
var audio = new Audio(url);
五、总结
📍标签与API并用
音频也可通过API实现。
📍浏览器多进程架构
浏览器架构趋向于面向服务,audio功能已经独立出来成为一个进程,vedio暂时还没有。