使用howler.js 在vue頁面播放音效

更新於 發佈於 閱讀時間約 1 分鐘

howler.js是一個強大的 JavaScript 音效庫,可以方便地在網頁上播放音效。

在 Vue.js 中使用 Howler.js 可以輕鬆地管理和播放音效。

  • npm安裝
npm install howler
  • 如果使用Typescript,由於Howler.js 沒有提供內置的 TypeScript 類型定義檔案,所以需要手動為 Howler 添加類型定義。
  1. 在項目根目錄下創建一個 types 文件夾(如果還沒有)。
  2. types 文件夾中創建一個 howler.d.ts 文件。
  3. howler.d.ts 文件中添加以下內容:
declare module 'howler' {
export class Howl {
constructor(options: HowlOptions);
play(sprite?: string | number): number;
pause(id?: number): void;
stop(id?: number): void;
mute(muted?: boolean, id?: number): void;
volume(volume?: number, id?: number): number;
fade(from: number, to: number, duration: number, id?: number): void;
rate(rate?: number, id?: number): number;
seek(seek?: number, id?: number): number;
loop(loop?: boolean, id?: number): boolean;
playing(id?: number): boolean;
duration(id?: number): number;
state(): string;
load(): void;
unload(): void;
on(event: string, handler: (id?: number) => void): void;
off(event: string, handler?: (id?: number) => void): void;
once(event: string, handler: (id?: number) => void): void;
}

export interface HowlOptions {
src: string | string[];
volume?: number;
html5?: boolean;
loop?: boolean;
preload?: boolean;
autoplay?: boolean;
mute?: boolean;
rate?: number;
pool?: number;
format?: string | string[];
xhrWithCredentials?: boolean;
onload?: () => void;
onloaderror?: (id: number, error: any) => void;
onplay?: (id: number) => void;
onend?: (id: number) => void;
onpause?: (id: number) => void;
onstop?: (id: number) => void;
onmute?: (id: number) => void;
onvolume?: (id: number) => void;
onrate?: (id: number) => void;
onseek?: (id: number) => void;
onfade?: (id: number) => void;
onunlock?: (id: number) => void;
}
}
  • 快速使用範例
<script setup lang="ts">
//引入 Howler.js
import { Howl } from 'howler';
//創建 Howl 實例:(此範例是將sample.mp3 放置於public/audio)
const sound = new Howl({ src: ['audio/sample.mp3'] });
//播放音效
const playSound = () => {
sound.play();
}
</script>
  • 基本使用範例
<template>
<div>
<button @click="playSound">播放音效</button>
<button @click="pauseSound">暫停音效</button>
<button @click="stopSound">停止音效</button>
</div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { Howl } from 'howler';

// 創建一個 ref 來保存 Howl 實例
const sound = ref<Howl | null>(null);

// 播放音效的方法
const playSound = () => {
if (!sound.value) {
sound.value = new Howl({
src: ['path/audio/sample.mp3']
});
}
sound.value.play();
};

// 暫停音效的方法
const pauseSound = () => {
if (sound.value) {
sound.value.pause();
}
};

// 停止音效的方法
const stopSound = () => {
if (sound.value) {
sound.value.stop();
}
};
</script>


  • 設置音量
const sound = new Howl({
src: ['/audio/sample.mp3'],
volume: 0.5 // 設置音量為 50%
});
  • 全局設置音量(需要引入Howler)
import { Howler } from 'howler';

// 設置全局音量為 50%
Howler.volume(0.5);

引入Howler,之前所提到的howler.d.ts 需要補以下內容

// 新增 Howler 全局對象的聲明
export const Howler: {
volume(volume?: number): number; // 控制全局音量
mute(muted: boolean): void; // 控制全局靜音
codecs(ext: string): boolean; // 檢查瀏覽器支持的音頻格式
stop(): void; // 停止所有音效
unload(): void; // 卸載所有音效
};
  • 控制全局音量範例
<template>
<div>
<button @click="playSound">播放音效</button>
<input type="range" min="0" max="1" step="0.1" v-model="volume"
@input="setGlobalVolume(volume)">
<label>全局音量: {{ volume }}</label>
</div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { Howl, Howler } from 'howler';

// 音量狀態,初始值為 0.5
const volume = ref(0.5);

// 用來儲存 Howl 實例的變數
const sound = ref<Howl | null>(null);

// 播放音效的函數
const playSound = () => {
if (!sound.value) {
sound.value = new Howl({
src: ['audio/sample.mp3']
});
}
sound.value.play();
};

// 設置全局音量的函數
const setGlobalVolume = (newVolume: number) => {
Howler.volume(newVolume);
};
</script>
  • 循環播放(加上loop: true ​)
// 播放循環音效的函數
const playLoopingSound = () => {
if (!sound.value) {
sound.value = new Howl({
src: ['path/to/your/audio/file.mp3'],
loop: true,
});
}
sound.value.play();
};
  • 監聽音效事件(onend, onplay, onpause, onstop, onloaderror)
<template>
<div>
<button @click="playSoundWithEvents">播放音效並監聽事件</button>
</div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { Howl } from 'howler';

// 儲存 Howl 實例的變數
const sound = ref<Howl | null>(null);

// 播放音效並監聽事件
const playSoundWithEvents = () => {
if (!sound.value) {
sound.value = new Howl({
src: ['audio/sample.mp3'],
onend: () => {
console.log('音效播放完畢');
},
onplay: () => {
console.log('音效開始播放');
},
onpause: () => {
console.log('音效暫停');
},
onstop: () => {
console.log('音效停止');
},
onloaderror: (id, error) => {
console.error('音效加載失敗', error);
}
});
}
sound.value.play();
};
</script>


  • 預加載音效

在 Howler.js 中,音效預加載是默認行為,當創建一個 Howl 實例時,音效文件就會開始加載。

可以通過 autoplay: false(默認)和 preload: true(默認)來控制的。

當 preload 設置為 true 時,音效會在創建 Howl 實例後自動預加載。

可以透過監聽 onload 事件來確定音效是否已經加載完畢。

<template>
<div>
<button @click="playSound" :disabled="!isLoaded">播放音效</button>
<p v-if="!isLoaded">音效加載中...</p>
<p v-if="isLoaded">音效已加載,點擊按鈕播放</p>
</div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { Howl } from 'howler';

// 音效加載狀態
const isLoaded = ref(false);

// 用來儲存 Howl 實例的變數
const sound = ref<Howl | null>(null);

// 創建 Howl 實例並預加載音效
sound.value = new Howl({
src: ['audio/sample.mp3'],
preload: true, // 預加載音效(默認是 true)
onload: () => {
isLoaded.value = true; // 當音效加載完畢時設置為 true
console.log('音效已加載');
},
onloaderror: (id, error) => {
console.error('音效加載失敗', error);
}
});

// 播放音效
const playSound = () => {
if (sound.value && isLoaded.value) {
sound.value.play();
}
};
</script>
  • 多個音效預加載
<script setup lang="ts">
import { onMounted } from 'vue';
import { Howl } from 'howler';

// 音效加載狀態
const allLoaded = ref(false);
const sounds: Howl[] = [];

// 在組件加載時預加載所有音效
onMounted(() => {
const soundFiles = [
'audio/1.mp3',
'audio/2.mp3',
'audio/3.mp3'
];

let loadedCount = 0;

soundFiles.forEach((src) => {
const sound = new Howl({
src: [src],
preload: true,
onload: () => {
loadedCount++;
if (loadedCount === soundFiles.length) {
allLoaded.value = true;
console.log('所有音效已加載');
}
},
onloaderror: (id, error) => {
console.error('音效加載失敗', error);
}
});
sounds.push(sound);
});
});
</script>

當所有音效文件都加載完畢時,更新 allLoaded 變數,表示所有音效已經準備好播放。

  • 懶加載
<template>
<div>
<button @click="playSound('audio1')">播放音效 1</button>
<button @click="playSound('audio2')">播放音效 2</button>
<button @click="playSound('audio3')">播放音效 3</button>
</div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { Howl } from 'howler';

// 儲存 Howl 實例的對象,按需創建和載入音效
const sounds: { [key: string]: Howl | null } = {
audio1: null,
audio2: null,
audio3: null,
};

// 音效的文件路徑
const soundFiles = {
audio1: 'audio/1.mp3',
audio2: 'audio/2.mp3',
audio3: 'audio/3.mp3',
};

// 播放音效的函數,按需加載音效
const playSound = (name: string) => {
if (!sounds[name]) {
// 懶加載音效:當音效第一次播放時才創建對應的 Howl 實例
sounds[name] = new Howl({
src: [soundFiles[name]],
preload: false, // 延遲加載音效,直到需要播放
onload: () => {
console.log(`${name} 已加載`);
},
onloaderror: (id, error) => {
console.error(`${name} 加載失敗`, error);
}
});
}
// 確保音效已經存在並開始播放
sounds[name]?.play();
};
</script>


  • 使用音效物件方式播放音效
import { Howl } from 'howler';

interface AudioManager {
sounds: { [key: string]: Howl };
preloadAll: () => void;
play: (name: string) => void;
playingBlockInstances: number;
maxBlockInstances: number;
}
const soundMuted = ref(false);
const isStop = ref(false);
const audioManager: AudioManager = {
// 宣告音效物件
sounds: {
error: new Howl({ src: ['/audio/error.mp3'] }),
win: new Howl({ src: ['/audio/win.mp3'] }),
click: new Howl({ src: ['/audio/click.mp3'] })
},
// 預加載
preloadAll() {
Object.values(this.sounds).forEach((sound) => {
sound.load();
});
},

// 播放音效函式
play(name: string) {
if (soundMuted) return;
const sound = this.sounds[name];
if (sound) {
const id = sound.play();
if(isStop.value) {
sound.stop(id);
}
} else {
console.error(`Sound ${name} not found!`);
}
},
};

// 播放音效的物件方法
const playSnd = {
win: () => audioManager.play('win'),
click () => audioManager.play('bet'),
err: () => audioManager.play('error')
};

playSnd.click(); //播放按鈕音效


  • 其他補充

sound.once 註冊一次性事件監聽器,只會觸發一次


留言
avatar-img
留言分享你的想法!
avatar-img
Ni 1968的沙龍
1會員
12內容數
愛看金庸的阿傑,想來分享一下我看金庸的點點滴滴。 講談金庸小說,免不了要爆一堆雷,所以請尚未看過金庸小說的朋友,跳過這裡,先去看金庸原著,享受金庸的武俠世界,別被爆雷破壞了第一次閱讀金庸小說的樂趣。 至於已看過金庸原著的朋友,歡迎一起分享你的觀點,小說劇情本就會各有解讀,各有領會,這才是現代文學的魅力!
Ni 1968的沙龍的其他內容
2024/12/21
介紹如何使用 JavaScript 中的陣列基本增刪元素方法,包括新增、刪除和替換元素的詳細說明。介紹的主要方法有 unshift、push、shift、pop、splice 和 slice,並解釋它們的使用場景和返回值。
Thumbnail
2024/12/21
介紹如何使用 JavaScript 中的陣列基本增刪元素方法,包括新增、刪除和替換元素的詳細說明。介紹的主要方法有 unshift、push、shift、pop、splice 和 slice,並解釋它們的使用場景和返回值。
Thumbnail
2024/11/02
在 Vue 應用中直接使用 .reverse() 來修改陣列可能在開發環境中未出現問題,但在生產環境中卻可能導致重新渲染錯誤及資料順序不一致的問題。建議使用 .slice() 創建淺拷貝的解決方案,以確保 Vue 的反應性系統能正常運作並避免應用當機。
Thumbnail
2024/11/02
在 Vue 應用中直接使用 .reverse() 來修改陣列可能在開發環境中未出現問題,但在生產環境中卻可能導致重新渲染錯誤及資料順序不一致的問題。建議使用 .slice() 創建淺拷貝的解決方案,以確保 Vue 的反應性系統能正常運作並避免應用當機。
Thumbnail
2024/08/08
如何在 Vite 專案中安裝和設置 TypeScript 及路徑別名的步驟,包括安裝必要的依賴、配置 vite.config.js、tsconfig.json 的設置,及如何創建類型聲明文件來正確識別 .vue 文件。
Thumbnail
2024/08/08
如何在 Vite 專案中安裝和設置 TypeScript 及路徑別名的步驟,包括安裝必要的依賴、配置 vite.config.js、tsconfig.json 的設置,及如何創建類型聲明文件來正確識別 .vue 文件。
Thumbnail
看更多
你可能也想看
Thumbnail
大家好,我是一名眼科醫師,也是一位孩子的媽 身為眼科醫師的我,我知道視力發展對孩子來說有多關鍵。 每到開學季時,診間便充斥著許多憂心忡忡的家屬。近年來看診中,兒童提早近視、眼睛疲勞的案例明顯增加,除了3C使用過度,最常被忽略的,就是照明品質。 然而作為一位媽媽,孩子能在安全、舒適的環境
Thumbnail
大家好,我是一名眼科醫師,也是一位孩子的媽 身為眼科醫師的我,我知道視力發展對孩子來說有多關鍵。 每到開學季時,診間便充斥著許多憂心忡忡的家屬。近年來看診中,兒童提早近視、眼睛疲勞的案例明顯增加,除了3C使用過度,最常被忽略的,就是照明品質。 然而作為一位媽媽,孩子能在安全、舒適的環境
Thumbnail
我的「媽」呀! 母親節即將到來,vocus 邀請你寫下屬於你的「媽」故事——不管是紀錄爆笑的日常,或是一直想對她表達的感謝,又或者,是你這輩子最想聽她說出的一句話。 也歡迎你曬出合照,分享照片背後的點點滴滴 ♥️ 透過創作,將這份情感表達出來吧!🥹
Thumbnail
我的「媽」呀! 母親節即將到來,vocus 邀請你寫下屬於你的「媽」故事——不管是紀錄爆笑的日常,或是一直想對她表達的感謝,又或者,是你這輩子最想聽她說出的一句話。 也歡迎你曬出合照,分享照片背後的點點滴滴 ♥️ 透過創作,將這份情感表達出來吧!🥹
Thumbnail
先前提到 Quasar 的 Dialog Plugin 很好用,再讓我補充一個用法。
Thumbnail
先前提到 Quasar 的 Dialog Plugin 很好用,再讓我補充一個用法。
Thumbnail
Quasar Dialog 的 Invoking custom component 很好用,但是有些困擾的地方,一起來看看有甚麼辦法吧。
Thumbnail
Quasar Dialog 的 Invoking custom component 很好用,但是有些困擾的地方,一起來看看有甚麼辦法吧。
Thumbnail
前言 從零開始構建一個 DateTimePicker 可能看起來令人畏懼,但試想一下你將獲得的靈活性和控制力。在這個系列中,我們將逐步揭開構建過程的神秘面紗,讓您能夠創建一個完全符合需求的自定義 DateTimePicker。 本文章,屬於付費系列的文章,這篇文章,我會希望讀者可以得到的
Thumbnail
前言 從零開始構建一個 DateTimePicker 可能看起來令人畏懼,但試想一下你將獲得的靈活性和控制力。在這個系列中,我們將逐步揭開構建過程的神秘面紗,讓您能夠創建一個完全符合需求的自定義 DateTimePicker。 本文章,屬於付費系列的文章,這篇文章,我會希望讀者可以得到的
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找
Thumbnail
Swiper.js 是一個功能齊全的輪播套件,除了輪播外,也可以客製化導航按鈕和頁碼等細項。目前支持 JS、React、Vue。但是 Swiper.js Vue 版本主要由 Composition API 風格構成,此篇文章將介紹 Options API 的撰寫方式,以及如何做樣式客製化。
Thumbnail
Swiper.js 是一個功能齊全的輪播套件,除了輪播外,也可以客製化導航按鈕和頁碼等細項。目前支持 JS、React、Vue。但是 Swiper.js Vue 版本主要由 Composition API 風格構成,此篇文章將介紹 Options API 的撰寫方式,以及如何做樣式客製化。
Thumbnail
新增video_player 在pubspec.yaml中加入video_player。 設置權限 Android 在AndroidManifest.xml檔案中的<application>裡,加入下列代碼。 <uses-permission android:name="androi
Thumbnail
新增video_player 在pubspec.yaml中加入video_player。 設置權限 Android 在AndroidManifest.xml檔案中的<application>裡,加入下列代碼。 <uses-permission android:name="androi
Thumbnail
Vue3 筆記,指令進階篇
Thumbnail
Vue3 筆記,指令進階篇
Thumbnail
Vue3 學習筆記,vue-router 篇。
Thumbnail
Vue3 學習筆記,vue-router 篇。
Thumbnail
Vue3 學習筆記,專案建立與基礎響應式篇
Thumbnail
Vue3 學習筆記,專案建立與基礎響應式篇
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News