跨瀏覽器音訊基礎
本文提供
- 一個建立跨瀏覽器 HTML 音訊播放器的基本指南,其中解釋了所有相關的屬性、特性和事件
- 一個使用 Media API 建立自定義控制元件的指南
基本音訊示例
以下程式碼是使用 HTML5 實現基本音訊的示例
<audio controls>
<source src="audio-file.mp3" type="audio/mpeg" />
<source src="audio-file.ogg" type="audio/ogg" />
<!-- fallback for non-supporting browsers goes here -->
<p>
Your browser does not support HTML audio, but you can still
<a href="audio-file.mp3">download the music</a>.
</p>
</audio>
注意:您也可以使用 MP4 檔案代替 MP3。MP4 檔案通常包含 AAC 編碼的音訊。您可以使用 type="audio/mp4"。(目前,支援 mp3 的瀏覽器也支援 mp4 音訊)。
-
這裡我們定義了一個帶有多個源的
<audio>元素 — 我們這樣做是因為並非所有瀏覽器都支援相同的音訊格式。為了確保合理的覆蓋範圍,我們應該至少指定兩種不同的格式。能提供最大覆蓋範圍的兩種格式是 mp3 和 ogg vorbis。 -
我們透過使用
<source>元素來做到這一點,該元素帶有src和type屬性。src包含要載入的音訊檔案的路徑(相對或絕對)。type用於通知瀏覽器檔案型別。如果省略,大多數瀏覽器會嘗試從副檔名猜測。
-
如果不支援
<audio>元素,那麼<audio>和<source>將被忽略。但是,您在<audio>元素中定義的任何受支援的文字或元素都將被顯示或執行。因此,建立備用方案或告知不相容性的理想位置是在結束</audio>標籤之前。在這種情況下,我們提供了一個段落,其中包含一個直接下載音訊的連結。 -
當我們需要瀏覽器為我們提供預設播放控制元件時,在
<audio>元素上指定controls屬性。如果您不指定此屬性,則不會出現任何控制元件——您將不得不建立自己的控制元件並使用 Media API 程式設計其功能(見下文)。然而,這可能是一個好方法,因為預設控制元件在不同瀏覽器中的外觀不同。因此,建立自己的控制元件可以確保所有瀏覽器上的控制元件外觀一致。
HTML 音訊詳解
現在我們已經看了一個基本示例,接下來讓我們更詳細地探討 HTML 音訊的不同方面。
音訊 HTML 屬性
我們可以為音訊元素指定多個屬性,以進一步決定音訊的初始化方式。
autoplay
指定 autoplay 將導致音訊儘快播放,無需任何使用者互動——簡而言之,音訊將自動播放。
<audio autoplay>…</audio>
注意:此值在移動平臺上通常會被忽略,並且除非確實必要,否則不建議使用。自動播放音訊(和影片)通常非常煩人。此外,瀏覽器有政策會在許多情況下完全阻止自動播放。有關詳細資訊,請參閱媒體和 Web Audio API 的自動播放指南。
loop
loop 屬性將確保當音訊剪輯播放到末尾時,音訊剪輯將迴圈回到開頭並重新開始播放。
<audio loop>…</audio>
muted
如果您希望音訊一開始就靜音(沒有音量),請新增 muted 屬性。
<audio muted>…</audio>
注意:此值在移動平臺上通常會被忽略。
preload
preload 屬性允許您指定瀏覽器預載入音訊的首選項,換句話說,當 <audio> 元素初始化時,以及在按下播放按鈕之前,它會下載檔案的哪個部分。
preload 可以採用 3 種不同的值
none:在按下播放按鈕之前不下載任何內容。metadata:下載音訊元資料;這通常是最佳選項,因為它允許您訪問和顯示音訊長度等資訊,並允許瀏覽器確定它應該使用哪個音訊檔案。auto:儘快下載整個音訊檔案。這通常不是一個好的選項,除非您能保證您的使用者將擁有快速的網路連線。
注意:此值在移動平臺上通常會被忽略。
<audio preload="auto">…</audio>
controls
當我們需要瀏覽器為我們提供其預設播放控制元件時,我們指定 controls 屬性。
<audio controls>…</audio>
src
如上所述,您可以使用 <source> 元素來指定一個或多個源音訊檔案。或者,您可以直接在 <audio> 元素上包含 src 屬性來指定單個原始檔。
<audio src="audio-file.mp3">…</audio>
type
如上所述,為了確保瀏覽器知道正在指定的檔案型別,最佳實踐是與 src 屬性一起指定 type 屬性。type 屬性指定檔案的 MIME 型別或 Internet Media Type。
<audio src="audio-file.mp3" type="audio/mpeg">…</audio>
使用 JavaScript 操作音訊元素
除了能夠在 HTML 中指定各種屬性外,<audio> 元素還附帶了一些可以透過 JavaScript 操作的屬性和方法。
給定以下 HTML
<audio id="my-audio" src="audio-file.mp3">…</audio>
您可以這樣獲取 <audio> 元素
const audio = document.getElementById("my-audio");
或者,您可以建立一個新元素。這是一個建立 <audio> 元素,設定媒體播放,播放和暫停,然後從音訊的 5 秒處開始播放的示例
const audio = document.createElement("audio");
if (audio.canPlayType("audio/mpeg")) {
audio.setAttribute("src", "audio-file.mp3");
}
if (audio.canPlayType("audio/ogg")) {
audio.setAttribute("src", "audio-file.ogg");
}
alert("play");
audio.play();
alert("stop");
audio.pause();
alert("play from 5 seconds in");
audio.currentTime = 5;
audio.play();
讓我們更詳細地探討可用的屬性和方法。
play
play() 方法用於指示音訊播放。它不帶任何引數。
audio.play();
pause
pause() 方法用於指示音訊暫停。它不帶任何引數。
audio.pause();
注意:沒有 stop 方法——要實現停止功能,您必須暫停媒體,然後將 currentTime 屬性值設定為 0。
canPlayType
canPlayType() 方法詢問瀏覽器是否支援某種音訊檔案型別。它將要檢查的 mime 型別作為引數。
if (audio.canPlayType("audio/mpeg")) {
// It's supported.
// Do something here!
}
canPlayType() 返回以下三個值之一
大概可能- ""(一個空字串)
實際上,我們通常檢查結果是真還是假。非空字串為真。
注意:一個很早期的規範規定瀏覽器應該返回 no 而不是空字串,但幸運的是,使用實現此版本規範的舊版瀏覽器的人很少。
currentTime
currentTime 屬性獲取或設定音訊應播放的當前時間。這在許多方面都很有用,例如,由於 play() 不帶引數,如果我們不想從 0 開始播放,我們需要單獨設定播放點。
currentTime 的值是一個數字,表示以秒為單位的時間。
if (audio.currentTime > 5) {
audio.currentTime = 3;
}
volume
volume 屬性允許我們將音訊音量設定為 0 到 1 之間的數字。
// set the volume at 50%
audio.volume = 0.5;
建立您自己的自定義音訊播放器
JavaScript 媒體 API 允許您建立自己的自定義播放器。讓我們看一個非常簡單的示例。我們可以結合 HTML 和 JavaScript 來建立一個帶有播放和暫停按鈕的播放器。首先,我們將在 HTML 中設定音訊,不帶 controls 屬性,因為我們正在建立自己的控制元件
<audio id="my-audio">
<source src="audio-file.mp3" type="audio/mpeg" />
<source src="audio-file.ogg" type="audio/ogg" />
<!-- place fallback here as <audio> supporting browsers will ignore it -->
<p>Download<a href="audio-file.mp3">audio-file.mp3</a></p>
</audio>
<!-- custom play and pause buttons -->
<button id="play">play</button>
<button id="pause">pause</button>
接下來,我們使用 JavaScript 為播放器新增一些功能
const audio = document.getElementById("my-audio");
const play = document.getElementById("play");
const pause = document.getElementById("pause");
// associate functions with the 'onclick' events
play.onclick = playAudio;
pause.onclick = pauseAudio;
function playAudio() {
audio.play();
}
function pauseAudio() {
audio.pause();
}
媒體載入事件
上面我們已經展示瞭如何建立一個音訊播放器,但是如果我們想顯示進度、緩衝,並且只在媒體準備好播放時啟用按鈕呢?幸運的是,我們有許多事件可以使用,讓我們的播放器準確地知道發生了什麼。
首先,讓我們按順序檢視媒體載入過程
loadstart
loadstart 事件告訴我們載入過程已開始,瀏覽器正在連線到媒體。
audio.addEventListener("loadstart", () => {
// Grabbing the file
});
durationchange
如果您只想儘快知道媒體的持續時間是否已確定,那麼這就是適合您的事件。這可能很有用,因為持續時間的初始值為 NaN(非數字),您可能不想將其顯示給您的使用者。
audio.addEventListener("durationchange", () => {
// You can display the duration now
});
loadedmetadata
元資料可以包含的不僅僅是持續時間——如果您想等到所有元資料下載完畢後再執行某些操作,您可以檢測 loadedmetadata 事件。
audio.addEventListener("loadedmetadata", () => {
// You can display the duration now
});
loadeddata
當媒體的第一部分到達時,會觸發 loadeddata 事件。播放頭已就位但尚未完全準備好播放。
audio.addEventListener("loadeddata", () => {
// You could display the playhead now
});
progress
progress 事件表示媒體的下載仍在進行中。此時顯示某種“載入器”是一個好習慣。
audio.addEventListener("progress", () => {
// you could let the user know the media is downloading
});
canplay
canplay 是一個有用的事件,如果您想確定媒體是否已準備好播放,則應檢測此事件。例如,您可以在此事件發生之前停用自定義控制元件。
audio.addEventListener("canplay", () => {
// Audio is ready to play
});
canplaythrough
canplaythrough 類似於 canplay,但它會告訴您媒體已準備好全程播放(也就是說,檔案已完全下載,或者預計它會及時下載,從而不會發生緩衝停止)。
audio.addEventListener("canplaythrough", () => {
// Audio is ready to play all the way through
});
媒體載入事件順序
回顧一下,媒體載入事件的順序是
loadstart > durationchange > loadedmetadata > loadeddata > progress > canplay > canplaythrough
載入中斷事件
我們還有一些事件可用,當媒體載入過程出現某種中斷時,這些事件會觸發。
媒體播放事件
我們還有另一組事件,這些事件對於響應媒體播放狀態很有用。
timeupdate
每次 currentTime 屬性更改時都會觸發 timeupdate 事件。實際上,這每 250 毫秒發生一次。此事件可用於觸發播放進度的顯示。
audio.addEventListener("timeupdate", () => {
// Update something related to playback progress
});
playing
當播放因缺少媒體資料而暫停後準備開始時,會啟動 playing 事件。
waiting
當播放因缺少媒體資料而停止時,會觸發 waiting 事件,儘管預計一旦資料可用就會恢復播放。
play
在 play() 方法返回後,或者當 autoplay 屬性導致播放開始時,會啟動 play 事件。這是媒體狀態從暫停切換到播放的時候。
pause
在 pause() 方法返回後觸發 pause 事件。這是狀態從播放切換到暫停的時候。
ended
當媒體播放結束時,會啟動 ended 事件。
audio.addEventListener("ended", () => {
// Do something once audio track has finished playing
});
volumechange
volumechange 事件表示音量已更改;這包括靜音。
帶反饋的音訊播放器
考慮這段 HTML 程式碼
<audio id="my-audio">
<source
src="http://jPlayer.org/audio/mp3/Miaow-07-Bubble.mp3"
type="audio/mpeg" />
<source
src="http://jPlayer.org/audio/ogg/Miaow-07-Bubble.ogg"
type="audio/ogg" />
<!-- place fallback here as <audio> supporting browsers will ignore it -->
<a href="audio-file.mp3">audio-file.mp3</a>
</audio>
<div id="controls">
<span id="loading">loading</span>
<button id="play">play</button>
<button id="pause">pause</button>
</div>
<div id="progress">
<div id="bar"></div>
</div>
樣式如下
#controls {
width: 80px;
float: left;
}
#progress {
margin-left: 80px;
border: 1px solid black;
}
#bar {
height: 20px;
background-color: green;
width: 0;
}
#play,
#pause {
display: none; /* hide until media is ready */
}
現在讓我們用 JavaScript 將它連線起來
const audio = document.getElementById("my-audio");
const play = document.getElementById("play");
const pause = document.getElementById("pause");
const loading = document.getElementById("loading");
const bar = document.getElementById("bar");
function displayControls() {
loading.style.display = "none";
play.style.display = "block";
}
// Check that the media is ready before displaying the controls
if (audio.paused) {
displayControls();
} else {
// not ready yet - wait for canplay event
audio.addEventListener("canplay", () => {
displayControls();
});
}
play.addEventListener("click", () => {
audio.play();
play.style.display = "none";
pause.style.display = "block";
});
pause.addEventListener("click", () => {
audio.pause();
pause.style.display = "none";
play.style.display = "block";
});
// Display progress
audio.addEventListener("timeupdate", () => {
// Sets the percentage
bar.style.width = `${Math.floor(
(audio.currentTime / audio.duration) * 100,
)}%`;
});
您最終應該得到類似這樣的結果

使用搜索欄進行搜尋
這是一個很好的開始,但如果能夠使用進度條導航音訊,那就更好了。幸運的是,這實現起來並不太困難。
首先,我們對進度條的 CSS 進行快速更新,以便在懸停時顯示手形指標
#progress {
margin-left: 80px;
border: 1px solid black;
cursor: pointer;
}
然後我們新增檢測點選並將“播放頭”移動到正確位置的程式碼
const progress = document.getElementById("progress");
progress.addEventListener("click", (e) => {
// Calculate the normalized position clicked
const clickPosition = (e.pageX - progress.offsetLeft) / progress.offsetWidth;
const clickTime = clickPosition * audio.duration;
// Move the playhead to the correct position
audio.currentTime = clickTime;
});
緩衝
好的,我們快完成了,但還有另一條有用的資訊可以顯示:已緩衝或預先下載的音訊量。
我們還沒有看過幾個屬性,buffered 和 seekable。
buffered
此屬性讓我們知道音訊的哪些部分已被緩衝(預先下載)。它返回一個名為 TimeRanges 的物件。
bufferedTimeRanges = audio.buffered;
seekable
seekable 屬性告知您是否可以直接跳轉到媒體的該部分而無需進一步緩衝。
seekableTimeRanges = audio.seekable;
緩衝事件
還有幾個與緩衝相關的事件
注意:您可以在其他地方閱讀更多關於緩衝、搜尋和時間範圍的資訊。
另見
- 緩衝、搜尋和時間範圍
- HTMLMediaElement 事件
- 事件參考 > 媒體
- HTML 影片和音訊
- 建立跨瀏覽器影片播放器
- jPlayer:一個用於 jQuery 和 Zepto 的開源音訊和影片庫。