跨浏览器音频基础

本文档提供

  • 创建跨浏览器 HTML 音频播放器的基本指南,其中解释了所有相关的属性、特性和事件
  • 使用媒体 API 创建自定义控件的指南

基本音频示例

以下代码是使用 HTML5 的基本音频实现示例

html
<audio controls>
  <source src="audiofile.mp3" type="audio/mpeg" />
  <source src="audiofile.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="audiofile.mp3">download the music</a>.
  </p>
</audio>

**注意:**您也可以使用 MP4 文件而不是 MP3。MP4 文件通常包含 AAC 编码的音频。您可以使用 type="audio/mp4"。(目前,支持 mp3 的浏览器也支持 mp4 音频)。

  • 这里,我们定义了一个 <audio> 元素,它具有多个源——我们这样做是因为并非所有浏览器都支持相同的音频格式。为了确保合理的覆盖范围,我们应该指定至少两种不同的格式。两种格式将提供最大覆盖范围的是 mp3 和 ogg vorbis。
  • 我们使用 <source> 元素来实现这一点,该元素接受 srctype 属性。
    • src 包含要加载的音频文件的路径(相对或绝对)。
    • type 用于告知浏览器文件类型。如果省略,大多数浏览器会尝试从文件扩展名中猜测它。
  • 如果 <audio> 元素不受支持,则 <audio><source> 将被忽略。但是,您在 <audio> 中定义的任何受支持的文本或元素都将被显示或执行操作。因此,创建回退或告知不兼容的理想位置是在结束 </audio> 标记之前。在本例中,我们提供了一个简单的段落,其中包含一个直接下载音频的链接。
  • 当我们要求浏览器为我们提供默认播放控件时,会指定 <audio> 元素上的 controls 属性。如果您未指定此属性,则不会显示任何控件——您将不得不使用媒体 API 创建自己的控件并对其功能进行编程(见下文)。但是,这可能是一种好方法,因为默认控件在各种浏览器中看起来不同。因此,创建自己的控件可以确保控件在所有浏览器中都具有一致的外观。

HTML 音频详解

现在,我们已经查看了一个基本示例,现在让我们更详细地探索 HTML 音频的不同方面。

音频 HTML 属性

我们可以使用音频标签指定许多属性,以进一步确定音频的初始化方式。

autoplay

指定 autoplay 将导致音频尽快开始播放,无需任何用户交互——简而言之,音频将自动播放。

html
<audio autoplay></audio>

**注意:**此值在移动平台上经常被忽略,除非确实需要,否则不建议使用它。自动播放音频(和视频)通常非常烦人。此外,浏览器具有策略,在许多情况下会完全阻止自动播放。有关详细信息,请参见 媒体和 Web 音频 API 的自动播放指南

loop

loop 属性将确保在到达音频片段末尾时,音频片段将循环回开头并再次开始播放。

html
<audio loop></audio>

muted

如果您希望音频静音开始播放(无音量),请添加 muted 属性。

html
<audio muted></audio>

**注意:**此值在移动平台上经常被忽略。

preload

preload 属性允许您指定浏览器预加载音频的偏好,换句话说,它在初始化 <audio> 元素时以及在按下播放按钮之前下载文件的哪一部分。

preload 可以采用 3 个不同的值

  1. none:在按下播放按钮之前不要下载任何内容。
  2. metadata:下载音频元数据;这通常是最佳选择,因为它允许您访问和显示音频长度等信息,并允许浏览器确定它应该使用哪个音频文件。
  3. auto:尽快下载整个音频文件。除非您能保证用户拥有快速的网络连接,否则这通常不是一个好选择。

**注意:**此值在移动平台上经常被忽略。

html
<audio preload="auto"></audio>

controls

当我们要求浏览器为我们提供其默认播放控件时,我们指定 controls 属性。

html
<audio controls></audio>

src

如上所述,您可以使用 <source> 元素指定一个或多个源音频文件。或者,您可以在 <audio> 元素上直接包含 src 属性以指定单个源文件。

html
<audio src="audiofile.mp3"></audio>

type

如上所述,为了确保浏览器知道正在指定的文件类型,最好在 src 属性旁边指定 type 属性。type 属性指定文件的 MIME 类型或 Internet 媒体类型。

html
<audio src="audiofile.mp3" type="audio/mpeg"></audio>

使用 JavaScript 操作音频元素

除了能够在 HTML 中指定各种属性之外,<audio> 元素还附带了一些您可以通过 JavaScript 操作的属性和方法。

假设以下 HTML

html
<audio id="my-audio" src="audiofile.mp3"></audio>

您可以像这样获取 <audio> 元素

js
const audio = document.getElementById("my-audio");

或者,您可以创建一个新元素。以下是如何创建 <audio> 元素、设置要播放的媒体、播放和暂停,以及从音频的 5 秒处开始播放的示例

js
const audio = document.createElement("audio");

if (audio.canPlayType("audio/mpeg")) {
  audio.setAttribute("src", "audiofile.mp3");
}

if (audio.canPlayType("audio/ogg")) {
  audio.setAttribute("src", "audiofile.ogg");
}

alert("play");

audio.play();

alert("stop");

audio.pause();

alert("play from 5 seconds in");

audio.currentTime = 5;
audio.play();

让我们更详细地探索可用的属性和方法。

play

play() 方法用于告诉音频播放。它不接受任何参数。

js
audio.play();

pause

pause() 方法用于告诉音频暂停。它不接受任何参数。

js
audio.pause();

**注意:**没有停止方法——要实现停止功能,您必须暂停媒体,然后将 currentTime 属性值设置为 0。

canPlayType

canPlayType() 方法询问浏览器是否支持某种音频文件类型。它将要检查的类型的 mime 类型作为参数。

js
if (audio.canPlayType("audio/mpeg")) {
  // It's supported.
  // Do something here!
}

canPlayType() 返回以下三个值之一

  1. probably
  2. maybe
  3. ""(空字符串)

在实践中,我们通常检查结果是否为真或假。非空字符串为真。

**注意:**非常早期的规范规定浏览器应该返回 no 而不是空字符串,但值得庆幸的是,使用实现此规范版本的老式浏览器的用户数量很少。

currentTime

currentTime 属性获取或设置音频应播放的当前时间。这在许多方面都很有用,例如,由于 play() 不接受参数,因此如果我们不希望它为 0,则需要单独设置要播放的点。

currentTime 的值为一个数字,表示以秒为单位的时间。

js
if (audio.currentTime > 5) {
  audio.currentTime = 3;
}

volume

volume 属性允许我们设置音频音量,这是一个介于 0 和 1 之间的数字。

js
// set the volume at 50%
audio.volume = 0.5;

创建您自己的自定义音频播放器

JavaScript 媒体 API 允许您创建自己的自定义播放器。让我们看一个非常简单的示例。我们可以将 HTML 和 JavaScript 结合起来,创建一个带有一个播放按钮和一个暂停按钮的非常简单的播放器。首先,我们将设置 HTML 中的音频,不带 controls 属性,因为我们将创建自己的控件

html
<audio id="my-audio">
  <source src="audiofile.mp3" type="audio/mpeg" />
  <source src="audiofile.ogg" type="audio/ogg" />
  <!-- place fallback here as <audio> supporting browsers will ignore it -->
  <p>Download<a href="audiofile.mp3">audiofile.mp3</a></p>
</audio>

<!-- custom play and pause buttons -->
<button id="play">play</button>
<button id="pause">pause</button>

接下来,我们使用 JavaScript 将一些功能附加到播放器

js
window.onload = () => {
  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 事件告诉我们加载过程已开始,并且浏览器正在连接到媒体。

js
audio.addEventListener("loadstart", () => {
  //grabbing the file
});

durationchange

如果您只想在媒体时长确定后立即知道,那么此事件适合您。这很有用,因为时长的初始值为 NaN(非数字),您可能不想将其显示给用户。

js
audio.addEventListener("durationchange", () => {
  //you can display the duration now
});

loadedmetadata

元数据可能包含时长以外的内容 - 如果您想等待所有元数据下载后再执行任何操作,您可以检测 loadedmetadata 事件。

js
audio.addEventListener("loadedmetadata", () => {
  //you can display the duration now
});

loadeddata

loadeddata 事件在收到第一个媒体片段时触发。播放头已就位,但尚未准备好播放。

js
audio.addEventListener("loadeddata", () => {
  //you could display the playhead now
});

progress

progress 事件表示媒体下载仍在进行中。最好在此处显示某种“加载”指示。

js
audio.addEventListener("progress", () => {
  // you could let the user know the media is downloading
});

canplay

canplay 是一个有用的事件,如果您想确定媒体是否已准备好播放,可以使用它。例如,您可以在此事件发生之前禁用自定义控件。

js
audio.addEventListener("canplay", () => {
  //audio is ready to play
});

canplaythrough

canplaythroughcanplay 类似,但它让您知道媒体已准备好播放完整内容(即文件已完全下载,或估计它将在缓冲停止发生之前完成下载)。

js
audio.addEventListener("canplaythrough", () => {
  //audio is ready to play all the way through
});

媒体加载事件顺序

回顾一下,媒体加载事件的顺序是

loadstart > durationchange > loadedmetadata > loadeddata > progress > canplay > canplaythrough

加载中断事件

我们还有一些可用的事件,当媒体加载过程出现某种中断时会触发。

suspend

即使文件尚未完全下载,也停止获取媒体数据。

abort

媒体数据下载已被中止,但不是由于错误。

error

在下载媒体数据时遇到错误。

emptied

媒体缓冲区已清空,可能是由于错误或调用了 load() 方法重新加载它。

stalled

媒体数据意外不可用。

媒体播放事件

我们还有一组事件,它们对响应媒体播放状态很有用。

timeupdate

timeupdate 事件在 currentTime 属性每次更改时触发。实际上,这每 250 毫秒发生一次。此事件可用于触发显示播放进度。

js
audio.addEventListener("timeupdate", () => {
  //update something related to playback progress
});

playing

playing 事件在由于缺少媒体数据而暂停后,播放准备开始时启动。

waiting

waiting 事件在由于缺少媒体数据而停止播放时触发,尽管一旦数据可用,预计会恢复播放。

play

play 事件在 play() 方法返回后或 autoplay 属性导致播放开始时启动。这是媒体状态从暂停切换到播放的时间。

pause

pause 事件在 pause() 方法返回后触发。这是状态从播放切换到暂停的时间。

ended

ended 事件在媒体结束时启动。

js
audio.addEventListener("ended", () => {
  //do something once audio track has finished playing
});

volumechange

volumechange 事件表示音量已更改;这包括静音。

带有反馈的音频播放器

考虑以下 HTML 代码片段

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="audiofile.mp3">audiofile.mp3</a>
</audio>

<div id="controls">
  <span id="loading">loading</span>
  <button id="play" style="display:none">play</button>
  <button id="pause" style="display:none">pause</button>
</div>
<div id="progress">
  <div id="bar"></div>
</div>

样式如下

css
#controls {
  width: 80px;
  float: left;
}

#progress {
  margin-left: 80px;
  border: 1px solid black;
}

#bar {
  height: 20px;
  background-color: green;
  width: 0;
}

现在让我们使用 JavaScript 连接它

js
window.onload = () => {
  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,
    )}%`;
  });
};

您最终应该得到类似以下的内容

A basic audio player with play/pause button and seek bar

使用进度条进行拖动

这是一个良好的开端,但能够使用进度条导航音频会更好。幸运的是,这并不难实现。

首先,我们对进度条 CSS 进行快速更新,以便在悬停时显示手形指针

css
#progress {
  margin-left: 80px;
  border: 1px solid black;
  cursor: pointer;
}

然后,我们添加检测点击并将“播放头”移动到正确位置的代码

js
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;
});

缓冲

好的,我们正在接近目标,但还有一个有用的信息可以显示:音频预先缓冲或下载了多少内容。

我们还没有研究过几个属性,bufferedseekable

buffered

此属性让我们知道音频的哪些部分已缓冲(预先下载)。它返回一个名为 TimeRanges 的对象。

js
bufferedTimeRanges = audio.buffered;

seekable

seekable 属性会告知您是否可以在不进行进一步缓冲的情况下直接跳到媒体的该部分。

js
seekableTimeRanges = audio.seekable;

缓冲事件

还有一些与缓冲相关的事件

seeking

当媒体正在被拖动时,seeking 事件被触发。

seeked

seekedseeking 属性更改为 false 时发生。

注意:您可以在其他地方阅读有关 缓冲、拖动和时间范围 的更多信息。

另请参见