跨浏览器音频基础
本文提供
- 一个创建跨浏览器 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 的开源音频和视频库。