第 6 课 7 / 18 | 编码:L06-10
核心知识

常用媒体事件

播放状态变化会触发事件,可用于更新页面文字、按钮或统计。

video 指向视频元素;state、duration、time 分别指向显示状态、总时长和当前位置的元素。addEventListener 注册监听器,事件发生时执行函数中的代码。

① 读取时长:元数据加载完成

video.addEventListener('loadedmetadata', () => {
  duration.textContent = video.duration.toFixed(1);
});

元数据是总时长、视频尺寸等基本信息。loadedmetadata 表示这些信息已经就绪,此时读取 video.duration,单位是秒;toFixed(1) 保留一位小数。

preload="metadata" 提示浏览器预加载元数据;loadedmetadata 报告元数据已加载完成。前者不是强制命令,后者也不依赖必须写出前者。

② 播放与暂停:更新状态文字

video.addEventListener('play', () => {
  state.textContent = '已进入播放状态';
});
video.addEventListener('playing', () => {
  state.textContent = '正在播放';
});
video.addEventListener('pause', () => {
  state.textContent = '已暂停';
});

play 表示从暂停转为播放状态,但可能还在等待数据;playing 表示开始实际播放或在等待后恢复播放;pause 表示进入暂停状态。

点击右侧播放、暂停按钮,观察提示。数据充足时,前两条提示可能很快接连出现。

③ 时间更新:显示当前播放位置

video.addEventListener('timeupdate', () => {
  time.textContent = video.currentTime.toFixed(1);
});

currentTime 是当前播放位置,单位是秒。textContent 将时间写入页面。正常播放和跳转都可能触发 timeupdate;它不是每秒恰好触发一次,也不是每一帧触发一次。

④ 跳转完成:位置变化不等于开始播放

video.addEventListener('seeked', () => {
  state.textContent = video.paused
    ? '跳转完成,仍处于暂停状态'
    : '跳转完成,处于播放状态';
});

seeked 表示定位到指定播放位置的操作已经完成。先暂停,再拖动进度条,观察当前位置改变后是否仍然暂停。

条件 ? 结果一 : 结果二 是条件表达式:video.paused 为真时选第一条提示,否则选第二条提示。

⑤ 等待、结束或失败:分别给出提示

video.addEventListener('waiting', () => {
  state.textContent = '等待媒体数据';
});
video.addEventListener('ended', () => {
  state.textContent = '播放结束';
});
video.addEventListener('error', () => {
  state.textContent = '视频无法加载或播放';
});

waiting 表示播放因暂时缺少数据而等待,不等于加载失败;ended 表示播放到结尾;error 表示媒体加载或播放发生错误。

可拖动到接近结尾后继续播放,观察结束提示。本地视频加载较快时,不一定能看到等待提示。

观察时长、位置与状态

在新标签页打开演示

展开完整 HTML 代码

保存为 demo/events.html,与视频位于同一文件夹。右侧演示使用这份代码。

<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>观察媒体事件</title>
</head>
<body>
  <video id="video" controls preload="metadata" width="320" playsinline></video>
  <p>总时长:<span id="duration">读取中</span> 秒</p>
  <p>当前位置:<span id="time">0.0</span> 秒</p>
  <p id="state" role="status">尚未播放</p>
  <script>
    const video = document.querySelector('#video');
    const state = document.querySelector('#state');
    const duration = document.querySelector('#duration');
    const time = document.querySelector('#time');

    // 元数据包括总时长、视频尺寸等信息。
    video.addEventListener('loadedmetadata', () => {
      duration.textContent = video.duration.toFixed(1);
    });
    video.addEventListener('play', () => {
      state.textContent = '已进入播放状态';
    });
    video.addEventListener('playing', () => {
      state.textContent = '正在播放';
    });
    video.addEventListener('pause', () => {
      state.textContent = '已暂停';
    });
    video.addEventListener('timeupdate', () => {
      time.textContent = video.currentTime.toFixed(1);
    });
    video.addEventListener('seeked', () => {
      state.textContent = video.paused
        ? '跳转完成,仍处于暂停状态'
        : '跳转完成,处于播放状态';
    });
    video.addEventListener('waiting', () => {
      state.textContent = '等待媒体数据';
    });
    video.addEventListener('ended', () => {
      state.textContent = '播放结束';
    });
    video.addEventListener('error', () => {
      state.textContent = '视频无法加载或播放';
    });

    // 先注册监听器,再设置路径,避免遗漏加载事件。
    // 此文件与视频都放在 demo 文件夹中。
    video.src = 'huangmei-opera.mp4';
  </script>
</body>
</html>