媒体属性与 API
HTML5 的 HTMLMediaElement API 提供了丰富的 JavaScript 接口来控制 <audio> 和 <video> 的播放行为。本节将介绍常用的媒体属性、方法和事件,帮助你构建自定义播放器。
前置知识
阅读本节前,建议先了解:track 字幕与文本轨道
基础概念
<audio> 和 <video> 元素共享同一个 JavaScript 接口 —— HTMLMediaElement。通过这个 API,你可以用 JavaScript 控制播放/暂停、调整音量、跳转播放位置、监听播放事件等,从而构建自定义的媒体播放器界面。
掌握这些 API 是构建高级媒体应用(如视频编辑器、自定义播放器、音频可视化等)的基础。
语法
基本控制方法
javascript
// 获取媒体元素
var video = document.getElementById('myVideo');
// 播放
video.play();
// 暂停
video.pause();
// 跳转到第 30 秒
video.currentTime = 30;
// 设置音量为 50%
video.volume = 0.5;
// 静音
video.muted = true;
// 切换播放速度(1.5 倍速)
video.playbackRate = 1.5;html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>媒体 API</title>
</head>
<body>
<h1>媒体 API 基础</h1>
<video id="myVideo" src="movie.mp4" width="400" height="225"></video>
<br>
<button onclick="playVideo()">播放</button>
<button onclick="pauseVideo()">暂停</button>
<button onclick="muteVideo()">静音/取消静音</button>
<script>
var video = document.getElementById('myVideo');
function playVideo() {
video.play();
}
function pauseVideo() {
video.pause();
}
function muteVideo() {
video.muted = !video.muted;
}
</script>
</body>
</html>详细说明
HTMLMediaElement 常用属性
| 属性 | 类型 | 说明 | 示例 |
|---|---|---|---|
currentTime | 数值 | 当前播放时间(秒) | video.currentTime = 30; |
duration | 数值 | 总时长(秒,只读) | var len = video.duration; |
paused | 布尔 | 是否暂停(只读) | if (video.paused) {...} |
ended | 布尔 | 是否播放结束(只读) | if (video.ended) {...} |
volume | 数值 | 音量(0.0~1.0) | video.volume = 0.5; |
muted | 布尔 | 是否静音 | video.muted = true; |
playbackRate | 数值 | 播放速率 | video.playbackRate = 1.5; |
buffered | 对象 | 已缓冲的时间范围 | video.buffered.end(0) |
readyState | 数值 | 就绪状态 | if (video.readyState >= 2) |
常用方法
| 方法 | 说明 |
|---|---|
play() | 开始播放(返回 Promise) |
pause() | 暂停播放 |
load() | 重新加载媒体源 |
canPlayType(type) | 检测是否支持指定格式 |
常用事件
| 事件 | 触发时机 |
|---|---|
play | 开始播放 |
pause | 暂停播放 |
ended | 播放结束 |
timeupdate | 播放位置更新 |
volumechange | 音量或静音状态改变 |
loadedmetadata | 元数据(时长、尺寸)加载完成 |
canplay | 有足够数据可以开始播放 |
waiting | 因缓冲不足而暂停 |
error | 加载出错 |
canPlayType 检测格式支持
javascript
var video = document.createElement('video');
// 检测是否支持 WebM VP9
var canWebM = video.canPlayType('video/webm; codecs=vp9');
// 返回值:"probably"、"maybe" 或 ""
// 检测是否支持 MP4 H.264
var canMP4 = video.canPlayType('video/mp4; codecs=avc1.42E01E');
console.log('WebM VP9:', canWebM); // "probably" 或 "maybe"
console.log('MP4 H.264:', canMP4); // "probably" 或 "maybe"实战示例
自定义视频播放器
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自定义播放器</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
max-width: 700px;
margin: 0 auto;
padding: 20px;
}
.player {
background: #111;
border-radius: 12px;
overflow: hidden;
}
.player video {
width: 100%;
display: block;
}
.controls {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
color: #fff;
}
.controls button {
background: none;
border: none;
color: #fff;
font-size: 20px;
cursor: pointer;
padding: 4px;
}
.progress-bar {
flex: 1;
height: 6px;
background: #444;
border-radius: 3px;
cursor: pointer;
position: relative;
}
.progress {
height: 100%;
background: #e74c3c;
border-radius: 3px;
width: 0%;
}
.time {
font-size: 13px;
color: #aaa;
white-space: nowrap;
min-width: 90px;
text-align: center;
}
.speed-btn {
font-size: 13px !important;
padding: 4px 8px !important;
background: #333 !important;
border-radius: 4px;
}
</style>
</head>
<body>
<div class="player">
<video id="video" src="movie.mp4" preload="metadata"></video>
<div class="controls">
<button id="playBtn" title="播放/暂停">▶</button>
<div class="progress-bar" id="progressBar">
<div class="progress" id="progress"></div>
</div>
<span class="time" id="timeDisplay">0:00 / 0:00</span>
<button class="speed-btn" id="speedBtn">1x</button>
<button id="muteBtn" title="静音">🔈</button>
</div>
</div>
<script>
var video = document.getElementById('video');
var playBtn = document.getElementById('playBtn');
var progressBar = document.getElementById('progressBar');
var progress = document.getElementById('progress');
var timeDisplay = document.getElementById('timeDisplay');
var speedBtn = document.getElementById('speedBtn');
var muteBtn = document.getElementById('muteBtn');
// 格式化时间
function formatTime(seconds) {
var mins = Math.floor(seconds / 60);
var secs = Math.floor(seconds % 60);
return mins + ':' + (secs < 10 ? '0' : '') + secs;
}
// 播放/暂停
playBtn.addEventListener('click', function() {
if (video.paused) {
video.play();
} else {
video.pause();
}
});
// 更新播放/暂停图标
video.addEventListener('play', function() {
playBtn.innerHTML = '▮▮'; // 暂停图标
});
video.addEventListener('pause', function() {
playBtn.innerHTML = '▶'; // 播放图标
});
// 更新进度条和时间
video.addEventListener('timeupdate', function() {
var pct = (video.currentTime / video.duration) * 100;
progress.style.width = pct + '%';
timeDisplay.textContent =
formatTime(video.currentTime) + ' / ' + formatTime(video.duration);
});
// 点击进度条跳转
progressBar.addEventListener('click', function(e) {
var rect = progressBar.getBoundingClientRect();
var pct = (e.clientX - rect.left) / rect.width;
video.currentTime = pct * video.duration;
});
// 播放速度切换
var speeds = [1, 1.25, 1.5, 2];
var speedIndex = 0;
speedBtn.addEventListener('click', function() {
speedIndex = (speedIndex + 1) % speeds.length;
video.playbackRate = speeds[speedIndex];
speedBtn.textContent = speeds[speedIndex] + 'x';
});
// 静音切换
muteBtn.addEventListener('click', function() {
video.muted = !video.muted;
muteBtn.innerHTML = video.muted ? '🔇' : '🔈';
});
// 播放结束
video.addEventListener('ended', function() {
playBtn.innerHTML = '▶';
progress.style.width = '0%';
});
</script>
</body>
</html>注意事项
play()方法返回 Promise,需要处理可能的拒绝(如浏览器阻止自动播放)currentTime和duration的单位是秒(浮点数)playbackRate设置过高可能导致音频失真- 事件监听器应尽早绑定(在 DOM 加载后立即绑定)
- 移动端浏览器的某些 API 行为可能不同
最佳实践
- 使用
play().catch()处理自动播放被阻止的情况 - 使用
timeupdate事件更新进度条(而非setInterval) - 用
canPlayType检测格式支持,提供合适的媒体源 - 使用
loadedmetadata事件获取视频时长后再初始化 UI - 自定义播放器时应保持键盘可访问性
- 使用
requestAnimationFrame处理与媒体同步的动画
下一节
继续学习:媒体格式兼容性