HTML - 如何高效使用 <audio> 标签
如果想要在页面中添加一段录音或音乐,有什么办法呢?本文将会介绍如何高效使用
<audio>
标签。
文章的英文版请戳我的 Medium . To read the article in English, please visit my Medium .
1.
<audio>
标签的基本用法
1.1
<audio>
标签的属性
我们来一起看看
<audio>
标签的常用属性:
-
src
:src
属性的值包含了嵌入音频的文件路径。 -
controls
: 如果controls
属性被声明,浏览器将提供一个包含声音、播放进度、播放暂停的控制面板,让用户可以控制音频的播放。 -
autoplay
: 如果autoplay
属性被声明,音频会尽快自动播放,不会等待整个音频文件下载完成。 -
loop
: 如果loop
属性被声明,将循环播放音频。 -
muted
: 如果muted
属性被声明,音频将被静音。 -
preload
:preload
属性的值示意了浏览器使用何种加载方式以达到最好的用户体验。它的值可以是none
(音频不会被缓存)、meta
(获取例如音频长度的音频元数据)或auto
(整个音频都将被加载)。如果autoplay
属性已经被声明时,preload
属性将被忽略。 -
disableRemotePlayback
: 如果disableRemotePlayback
属性被声明,浏览器将禁用远程设备上进行进度控制的能力。
<!-- 一个自动播放、初始静音、循环播放、有控制面板的音频播放器 -->
<audio
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
controls
autoplay
muted
浏览器不支持音频播放。
</audio>
如果想要看有关
<audio>
标签更多的示例代码,可以戳我的
CodePen
。
如果想要更完整地了解
<audio>
标签,可以看看
MDN - <audio>
。
1.2 如何预加载音频文件?
我们需要预加载音频文件吗?这完全取决于音频的作用。我们在 1.1 部分介绍了
preload
属性的 3 个值:
none
、
meta
和
auto
。 我们一起来看看何时使用哪个值:
-
none
:none
值表示了音频不会被预加载,这样会减少网络流量的使用。 一个典型的场景就是播客。当用户浏览一个列表的不同集数时,我们不需要准备所有的音频文件,只有当用户点击一个单集的“播放”按钮时,我们再去请求这个单集的音频数据。
2.
meta
:
meta
值表示了只获取音频的元数据。 一个典型的场景就是当你打开一个带有音频播放器的网页时,你可能不会马上开始播放音频,但我们需要相关的元数据来补充播放器的内容。例如,当你打开 Spotify 的主页时,页脚的播放器会展示你上一次播放的音频,但因为你也许会播放其它音频,所以这段音频不会直接被自动播放。
3.
auto
:
auto
值会在加载页面后加载整个音频文件。 一个典型的场景就是游戏。因为游戏过程中需要很多的背景音乐、声效等,所以在游戏加载时,我们也需要同时加载需要的音频文件。
为了解决一些移动设备 不支持
preload
属性, 我们可以使用
load()
方法来让
<audio>
元素加载音频。
<audio
id="audio-player"
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
preload="auto"
controls
浏览器不支持音频播放。
</audio>
window.addEventListener('load', () => {
document.queryselector('#audio-player').load();
});
1.3 音频文件格式
HTML 支持 3 种音频文件格式: OGG、 MP3 和 WAV。
- OGG: OGG 文件没有被压缩,所以音质是 3 种文件格式中最好的,但同时文件大小也是最大的。
- MP3: MP3 文件被压缩过,所以文件大小会略小一些,同时音质也会低于 OGG 文件。另外, MP3 格式是最普遍使用的文件格式。
- WAV: WAV 文件也是被压缩过,文件大小是 3 种文件格式中最小的,但音质也是最差的。
如果想要查看浏览器对 3 种音频文件格式的兼容性,可以查看
Can I use audio format?
。如果我们想要解决兼容性的问题,我们可以把三种文件格式都包含在
<audio>
标签里,让浏览器选择最合适的文件格式来播放。
<audio controls>
<!-- OGG 文件 -->
<source src="music.ogg" type="audio/ogg" />
<!-- MP3 文件 -->
<source src="music.mp3" type="audio/mpeg" />
<!-- WAV 文件 -->
<source src="music.wav" type="audio/wav" />
</audio>
引用
HTML5 audio标签使用 preload属性 规定是否在页面加载后载入音频
Smallest audio file: MP3, Ogg, or Wav?
2. 操作
<audio>
元素
2.1
HTMLAudioElement
接口
HTMLAudioElement
接口提供了可以访问
<audio>
元素的属性,以及操作它们的方法。
最常用的
<audio>
元素属性有:
-
src
:src
属性指定了元素中使用的音频资源的 URL。 -
currentTime
:currentTime
属性以秒为单位返回当前音频元素的播放时间。 -
duration
:duration
属性以秒为单位给出音频的长度。 -
paused
:paused
属性指出音频是否正在暂停。 -
playbackRate
:playbackRate
属性设置了音频文件播放时的速率。 -
muted
:muted
属性指出音频是否被静音。 -
volume
:volume
属性设置了音频播放时的音量。
最常用的操作
<audio>
元素的方法有:
-
load()
:load()
方法让<audio>
元素重新加载,让播放也回到起始点。 -
▶️
play()
:play()
方法尝试让音频开始播放。 -
⏸
pause()
:pause()
方法暂停音频播放。
如果想要更完整地了解
HTMLAudioElement
和
HTMLMediaElement
接口,可以看看
MDN - HTMLAudioElement
和
MDN - HTMLMediaElement
。
2.2
<audio>
标签的事件
<audio>
标签支持很多的事件来让我们了解音频的加载以及播放进度。最常用的事件有:
-
loadedmetadata
: 元数据加载完成。 -
canplay
: 浏览器已经可以播放音频,但是预测加载的数据不足以在不暂停的情况下顺利将其播放到结束。 -
canplaythrough
: 浏览器预测已经可以在不暂停的前提下将音频播放到结束。 -
stalled
: 用户代理是图获取音频数据,但数据意外地没有进入。 -
suspend
: 音频加载挂起。 -
play
: 播放开始。 -
pause
: 播放暂停。 -
waiting
: 因为暂时性缺少数据,播放暂停。 -
playing
: 因缺少数据而暂停或延迟的状态结束,播放准备开始。 -
seeking
: 一次获取操作开始。 -
seeked
: 一次获取操作结束。 -
ratechange
: 播放速率变化。 -
timeupdate
:HTMLAudioElement.currentTime
属性指定的时间更新。 -
volumechange
: 音量变化。 -
ended
: 播放到音频的结束为止,播放停止。
<audio
id="audio-player"
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
controls
浏览器不支持音频播放。
</audio>
const audio = document.querySelector('#audio-player');
// canplay
audio.addEventListener('canplay', () => {
alert('You can play the audio now.');
// play
audio.addEventListener('play', () => {
alert('The audio is playing now!');
// pause
audio.addEventListener('pause', () => {
alert('The audio is paused to play.');
// seeked
audio.addEventListener('seeked', () => {
alert(`You have changed the current playback time to ${Math.ceil(audio.currentTime)}s.`);
// timeupdate
audio.addEventListener('timeupdate', () => {
if (Math.ceil(audio.currentTime) % 30 === 0) {
alert(`The current playback time is ${Math.ceil(audio.currentTime / 60)}min(s).`);
// ended
audio.addEventListener('ended', () => {
alert('Audio finished playing.');
如果想要看更多有关
<audio>
标签事件的示例代码,可以戳我的
CodePen
。
2.3 在实际情况操作
<audio>
元素
使用
<audio>
标签时会有很多的兼容性问题,我们一起来看看这些问题和它们的解决办法。我们也会看到一些操作
<audio>
元素来增加用户体验的例子 。
1. 如何查看音频播放结束?
想要查看
<audio>
元素是否播放到结尾了,监听
ended
事件不够准确,我们可以通过监听
timeupdate
事件来查看
currentTime
的值是否已经大等于
duration
。
<audio
id="audio-player"
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
controls
浏览器不支持音频播放。
</audio>
const audio = document.querySelector('#audio-player');
audio.addEventListener('timeupdate', () => {
if (audio.currentTime >= audio.duration) {
alert('Audio finished playing');
});
2. 如何添加加载效果?
为了提高用户体验,我们可以在音频加载的过程中添加加载效果。我们可以通过监听
loadstart
和
loadeddata
事件来让显示加载效果,然后当
canplay
事件被触发时移除加载效果。
<audio
id="audio-player"
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
controls
浏览器不支持音频播放。
</audio>
const audio = document.querySelector('#audio-player');
let isLoading = false;
audio.addEventListener('loadstart', () => {
isLoading = true;
audio.addEventListener('loadeddata', () => {
isLoading = true;
audio.addEventListener('canplay', () => {
isLoading = false;
3. 如何自动播放音频?
为了解决一些移动设备 不支持
autoplay
属性,我们可以靠调用
play()
方法来播放音频。
<audio
id="audio-player"
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
controls
浏览器不支持音频播放。
</audio>
window.addEventListener('canplay', () => {
document.queryselector('#audio-player').play();
4. 如何连续播放多个音频资源?
一种方法是在音频播放结束的时候修改
src
属性的值。
<audio
id="audio-player"
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
controls
浏览器不支持音频播放。
</audio>
const audio = document.querySelector('#audio-player');
let songNum = 1;
audio.addEventListener('timeupdate', () => {
if (audio.currentTime >= audio.duration) {
songNum = songNum >= 17 ? 1 : songNum + 1;
audio.src = `https://www.soundhelix.com/examples/mp3/SoundHelix-Song-${songNum}.mp3`;
另一种方法是在一开始的时候通过多个
<audio>
标签预加载所有的音频资源,然后选择播放其中的一个。这会在一开始的时候消耗很多资源来加载多个音频文件,但会在切换音频文件的时候更快。
<audio
id="audio-player1"
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
controls
浏览器不支持音频播放。
</audio>
<audio
id="audio-player2"
style="display: none;"
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3"
controls
浏览器不支持音频播放。
</audio>
<audio
id="audio-player3"
style="display: none;"
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3"
controls
浏览器不支持音频播放。
</audio>
const audio1 = document.querySelector('#audio-player1');
const audio2 = document.querySelector('#audio-player2');
const audio3 = document.querySelector('#audio-player3');
audio1.addEventListener('timeupdate', () => {
if (audio1.currentTime >= audio1.duration) {
audio1.style.display = 'none';
audio2.style.display = 'block';
audio2.play();
audio2.addEventListener('timeupdate', () => {
if (audio2.currentTime >= audio2.duration) {
audio2.style.display = 'none';
audio3.style.display = 'block';
audio3.play();
audio3.addEventListener('timeupdate', () => {
if (audio3.currentTime >= audio3.duration) {
audio3.style.display = 'none';
audio1.style.display = 'block';
audio1.play();
引用
HTML5 Audio & Video - 兼容性总结(一)
3. 改变音频播放器的样式
现在让我们来看看如何改变
<audio>
元素控制面板的样式。
根据 MDN 的描述,
<audio>
元素没有自带的固有视觉样式,除非如果声明了controls
属性,则会显示浏览器的默认控件。
默认控件的display
的默认值为inline
。将该值设为block
通常会对定位和布局有好处,除非你想将控件放在文本块或类似元素中。
你可以使用作用于整个控件的属性来为其设置样式。例如可用border
、border-radius
、padding
,margin
等等。但你不能设置音频播放器中的单个组件(如改变按钮大小、改变图标或字体等)。控件在不同的浏览器中也有所不同。
我们将会介绍两种不同的方式来改变音频播放器的样式。
3.1 用 CSS 改变
<audio>
元素的样式
我们可以使用
<audio>
元素的伪元素来改变
<audio>
元素控制面板的样式。
⚠️ 注意: 只有以 WebKit 或 Blink 为内核的浏览器(例如 Safari 和 Chrome)才会支持 WebKit 伪元素。
-
::-webkit-media-controls-panel
: 控制面板的容器。 -
::-webkit-media-controls-mute-button
: 控制面板上的静音按钮。 -
::-webkit-media-controls-play-button
: 控制面板上的播放按钮。 -
::-webkit-media-controls-current-time-display
: 控制面板上展示当前播放时间的文字。 -
::-webkit-media-controls-time-remaining-display
: 控制面板上展示总播放时长的文字。 -
::-webkit-media-controls-timeline
: 控制面板上的播放时间线。 -
::-webkit-media-controls-volume-slider
: 控制面板上控制音量的滚动条,在 Chrome 上滚动条会出现在悬浮在静音按钮上时。
<audio
id="audio-player"
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
controls
浏览器不支持音频播放。
</audio>
#audio-player::-webkit-media-controls-panel {
background: lightblue;
#audio-player::-webkit-media-controls-current-time-display {
color: red;
font-weight: bold;
#audio-player::-webkit-media-controls-time-remaining-display {
color: gray;
}
如果想要查看更多关于
<audio>
标签伪元素的示例代码,可以戳我的
CodePen
。
如果想要查看
<audio>
标签伪元素的完整列表,可以看看
mediaControl.css
。
3.2 使用 JavaScript 和 CSS 自定义一个音频播放器
我们可以创造一个没有
controls
属性的
<audio>
元素,然后创造一些元素例如按钮等来操作音频。通过点击这些元素,我们可以改变音频的相关属性或是调用相关方法来完成操作。我们也可以通过监听音频时间来改变样式。
<div id="audio-player">
<!-- 音频资源 -->
<audio id="audio" src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" />
<!-- 时间线 -->
<div id="timeline">
<div id="total-time">
<div id="current-timeline" />
</div>
<div id="time-display">
<span id="current-time" />
<span id="duration" />
</div>
</div>
<!-- 操作音频 -->
<div id="operations">
<button id="play-button">
<img src="https://cdn-icons-png.flaticon.com/512/27/27223.png" />
</button>
<button id="pause-button">
<img src="https://cdn-icons-png.flaticon.com/512/64/64485.png" />
</button>
</div>
</div>
#audio-player {
width: 500px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
/* 时间线 */
#timeline {
width: 400px;
margin: 20px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
#total-time {
width: 100%;
height: 10px;
border-radius: 20px;
background: #063f60;
margin: 10px;
position: relative;
#current-timeline {
height: 10px;
border-radius: 20px 0 0 20px;
background: #87ffc3;
position: absolute;
top: 0;
left: 0;
#time-display {
width: 100%;
display: flex;
justify-content: space-between;
color: gray;
/* 操作音频 */
#operations {
display: flex;
justify-content: center;
button {
background: transparent;
border: 0;
width: 40px;
margin: 10px 20px;
button:hover {
cursor: pointer;
button > img {
width: 100%;
#pause-button {
display: none;
}
// 音频资源
const audio = document.querySelector('#audio');
// 操作
const playButton = document.querySelector('#play-button');
const pauseButton = document.querySelector('#pause-button');
audio.addEventListener('play', () => {
playButton.style.display = 'none';
pauseButton.style.display = 'block';
audio.addEventListener('pause', () => {
playButton.style.display = 'block';
pauseButton.style.display = 'none';
playButton.addEventListener('click', () => {
audio.play();
pauseButton.addEventListener('click', () => {
audio.pause();
// 时间线
const currentTimeText = document.querySelector('#current-time');
const currentTimeLine = document.querySelector('#current-timeline');
const durationElement = document.querySelector('#duration');
let currentTime = new Date(null);
let duration = new Date(null);
audio.addEventListener('loadedmetadata', () => {
duration = new Date(null);
duration.setSeconds(audio.duration);
durationElement.innerHTML = `${duration.toISOString().substring(11, 19)}`;
currentTime = new Date(null);
currentTime.setSeconds(0);
currentTimeText.innerHTML = `${currentTime.toISOString().substring(11, 19)}`;
audio.addEventListener('timeupdate', () => {