使用howler.js封装一个网页游戏音乐管理器
踩坑指南
虽然howler.js说他可以自动感知然后自动播放,事实上没什么软用,所以我们还需要自己去配置一下触发器。监听一个全局的click事件之类的。
虽然howler.js说可以自动降级到h5播放,但是实际测试无法在安卓4.4中进行播放,但是个人测试,如果你使用一个audio元素,是可以播放的,但是这样就需要自己去手动封装一个基于audio元素的播放管理,项目赶的很,实在没有多余的时间去研究这个,特别是音效的播放,还是专业的库处理比较好,所以暂时对于安卓4.4的兼容是没法做到的。
官方对于这个问题也是很模糊,找不到解决办法。
howler.js在文档中找了半天,也没看到官方提供动态替换资源的方法,导致如果你有多个音效,就不得不new出多个howler实例对象。
代码
先安装插件:
npm i howler.js -D
github:howler.js
首先我声明一下ts类型:
types.ts
import { Howl } from "howler";
import { EffectName } from "./config";
/** audio基类 */
export interface BaseAudio {
/** 播放的方法 */
play(name: EffectName): void;
/** 暂停的方法 */
pause(): void;
/** 继续播放 */
continuePlay(): void;
/** 静音 */
mute(status: boolean): void;
}
/** 音效实例map */
export type EffectMap = Map<EffectName, Howl>;
声明了一个基类BaseAudio,后面写的bgm和effect类都是基于该基类进行实现,这样外部就可以依赖于这个基类。
然后就是一些音乐的配置文件,里面有音效的枚举,各种音乐的资源地址:
config.ts
/** bgm配置 */
export const bgmConfig = {
src: "./audio/bgm.mp3",
};
/** 音效名称enum */
export enum EffectName {
button = "button",
number = "number",
lose = "lose",
win = "win",
draw = "draw",
}
/** 音效配置:根据配置生成不同的音效实例 */
export const effectConfig = [
{
src: "./audio/button.mp3",
name: EffectName.button,
},
{
src: "./audio/number.mp3",
name: EffectName.number,
},
{
src: "./audio/lose.mp3",
name: EffectName.lose,
},
{
src: "./audio/win.mp3",
name: EffectName.win,
},
{
src: "./audio/draw.mp3",
name: EffectName.draw,
},
];
现在配置都有了,我们创建一个core文件夹存放bgm和effect类。
core/audioBgm.ts
import { Howl } from "howler";
import { bgmConfig } from "../config";
import { BaseAudio } from "../types";
class AudioBgm implements BaseAudio {
/** 音乐实例 */
private static bgm: Howl | null = null;
/** 音乐配置 */
private config = {
// html5: true, //开了会导致默认静音再开启无法播放
autoplay: true,
loop: true,
volume: 1,
};
/** 音乐是否播放 */
public get isPlay() {
return AudioBgm.bgm?.playing() ?? false;
}
/** 是否已经监听了点击事件 */
public static isListen = false;
/** 是否静音 */
private isMute = false; //true静音,false取消静音
constructor() {
if (!AudioBgm.bgm) {
AudioBgm.bgm = new Howl({ ...this.config, ...bgmConfig });
}
//先触发play播放,因为知道是同一个浏览器页面,
//地址的变化不会影响用户在浏览器中的操作变量变化(判断是否用户主动触发)
this.play();
//如果没有播放则添加一个click事件来手动触发
//但是这个isPlay不是及时的,所以有可能过一会就播放了但是监听了事件
//所以监听的事件里自己再做个判断
if (!this.isPlay && !AudioBgm.isListen) {
this.interactionClick = this.interactionClick.bind(this);
this.listenClick();
}
}
/** 监听点击事件播放音乐 */
private listenClick() {
document.addEventListener("click", this.interactionClick);
}
/** 点击事件,用于触发交互,无交互无法正确播放 */
private interactionClick() {
if (!this.isPlay) {
this.play();
}
AudioBgm.isListen = true;
/** 销毁自己 */
document.removeEventListener("click", this.interactionClick);
}
/** 音乐播放 */
public play(): void {
if (this.isMute) return;
AudioBgm.bgm?.play();
}
/** 暂停 */
public pause(): void {
AudioBgm.bgm?.pause();
}
/** 恢复播放 */
public continuePlay(): void {
this.play();
}
/** 静音 */
public mute(status: boolean): void {
AudioBgm.bgm?.mute(status);
this.isMute = status;
//不静音自动播放
if (!this.isMute && !this.isPlay) {
this.play();
}
}
}
export default AudioBgm;
core/audioEffect.ts
import { Howl } from "howler";
import { effectConfig, EffectName } from "../config";
import { BaseAudio, EffectMap } from "../types";
class AudioEffect implements BaseAudio {
/** 是否初始化 */
public static isInit = false;
/** 音效实例 */
public static effectMap: EffectMap = new Map();
/** 音效配置 */
private config = {
// html5: true,
autoplay: false,
loop: false,
volume: 1,
};
/** 是否静音 */
private isMute = false; //true静音,false取消静音
constructor() {
if (!AudioEffect.isInit) {
this.initEffect();
}
}
/** 初始化音效 */
private initEffect() {
effectConfig.forEach((item) => {
AudioEffect.effectMap.set(item.name, new Howl({ ...this.config, ...{ src: item.src } }));
});
}
/** 播放音效 */
public play(name: EffectName): void {
if (!name) return;
if (this.isMute) return;
const findEffect = AudioEffect.effectMap.get(name);
if (findEffect) {
findEffect.play();
}
}
/** 暂停 */
public pause(): void {
AudioEffect.effectMap.forEach((effect) => {
effect.pause();
});
}
/** 继续播放 */
public continuePlay(): void {}
/** 静音 */
public mute(status: boolean): void {
AudioEffect.effectMap.forEach((effect) => {
effect.mute(status);
this.isMute = status;
});
}
}
export default AudioEffect;
音效和bgm类各有不同的处理,自行看代码吧!
所有的内容写好后,我们需要一个单例管理器来统一管理:
- bgm的播放在初始化后就需要播放
- 音效需要每次被调用方调用
index.ts
import AudioBgm from "./core/audioBgm";
import AudioEffect from "./core/audioEffect";
import { EffectName } from "./config";
class AudioManager {
private static audioBgm: AudioBgm;
private static audioEffect: AudioEffect;
private static initState = false;
constructor() {
if (!AudioManager.initState) {
AudioManager.audioBgm = new AudioBgm();
AudioManager.audioEffect = new AudioEffect();
AudioManager.initState = true;
}
}
/** 播放音效 */
public playEffect(name: EffectName): void {
AudioManager.audioEffect.play(name);
}
/** 静音 true静音,false取消静音 */
public mute(status: boolean): void {
AudioManager.audioBgm.mute(status);
AudioManager.audioEffect.mute(status);
}
}
export { AudioManager, EffectName };
export default AudioManager;
调用:
首次初始化我们可以放在vue的第一个vue组件,也就是App.vue中
<script lang="ts">
import { defineComponent, onMounted} from "vue";
import AudioManager from "@/utils/audio";
export default defineComponent({
setup() {
onMounted(() => {
/** 音乐 */
const audioManager = new AudioManager();
});
}
})
当我们需要触发音效的时候:
<script lang="ts">
import { defineComponent, onMounted} from "vue";
import { AudioManager, EffectName } from "@/utils/audio";
export default defineComponent({
setup() {
const audioManager = new AudioManager();
/** 点击事件 */
function onClick() {
audioManager.playEffect(EffectName.button);
}
return {
onClick
}
}
})
由于是单例模式,所以不用操心实例化的对象不是同一个,以及资源的浪费。
分类:
JavaScript
标签:
howler音乐管理器
版权申明
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
暂无评论数据