Web前端技术 · 第6课8 / 18 | 编码:L06-10a
理解规则,再判断结果

调用play,不等于已经播放成功

方法是主动调用;事件是浏览器报告发生了什么。状态应根据结果更新。

video 指向视频元素,state 指向显示状态的元素。下面按按钮功能阅读代码。

① 点击播放:等待结果,再显示提示

document.querySelector('#play').addEventListener('click', async () => {
  try {
    await video.play();
    state.textContent = '播放已启动';
  } catch (error) {
    state.textContent = '播放未成功:' + error.name;
  }
});

addEventListener('click', …) 指定按钮被点击时执行的函数。play() 请求播放,返回一个 Promise,表示这次请求将来可能成功,也可能失败。

async 允许函数中使用 await。这里等待的是播放成功启动,不是视频播放结束;等待期间网页仍能响应操作。

成功后执行下一行提示;失败则进入 catch,用 error.name 显示错误名称。

② 点击暂停:调用 pause()

document.querySelector('#pause').addEventListener('click', () => {
  video.pause();
});

pause() 暂停播放,不会把播放位置重置到开头。它不返回表示播放结果的 Promise,这里不需要 await。

③ 点击跳转:设置 currentTime

const seekButton = document.querySelector('#seek');
seekButton.addEventListener('click', () => {
  video.currentTime = Math.min(30, video.duration);
});

currentTime 是当前播放位置,duration 是总时长,单位都是秒。Math.min 取较小值:视频长 90 秒时跳到第 30 秒;长 20 秒时跳到结尾。

设置位置不等于开始播放。先暂停,再点击跳转按钮,观察时间和播放状态。

④ 何时允许跳转:先取得视频时长

<button id="seek" type="button" disabled>跳到第30秒</button>
function showMetadata() {
  document.querySelector('#duration').textContent =
    video.duration.toFixed(1);
  seekButton.disabled = false;
}
video.addEventListener('loadedmetadata', showMetadata);
if (video.readyState >= 1) showMetadata();

disabled 使按钮初始不可点击。元数据包含视频时长等基本信息;收到 loadedmetadata 后显示时长,并用 disabled = false 启用按钮。

toFixed(1) 保留一位小数。readyState >= 1 检查元数据是否已经就绪,避免注册监听器时事件已经发生而未更新界面。

用按钮控制播放与跳转

在新标签页打开演示

展开完整 HTML 代码

保存为 demo/methods.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>
  <!-- 本文件保存在 demo 中,与视频位于同一文件夹。 -->
  <video id="video" controls preload="metadata" width="320"
         src="huangmei-opera.mp4" playsinline></video>
  <p>
    <button id="play" type="button">播放</button>
    <button id="pause" type="button">暂停</button>
    <button id="seek" type="button" disabled>跳到第30秒</button>
  </p>
  <p id="state" role="status">尚未播放</p>
  <p>总时长:<span id="duration">读取中</span> 秒</p>
  <p>当前位置:<span id="time">0.0</span> 秒</p>
  <script>
    const video = document.querySelector('#video');
    const state = document.querySelector('#state');
    const seekButton = document.querySelector('#seek');

    document.querySelector('#play').addEventListener('click', async () => {
      try {
        await video.play();
        state.textContent = '播放已启动';
      } catch (error) {
        state.textContent = '播放未成功:' + error.name;
      }
    });
    document.querySelector('#pause').addEventListener('click', () => {
      video.pause();
    });
    seekButton.addEventListener('click', () => {
      // 视频不足30秒时,最多跳到结尾。
      video.currentTime = Math.min(30, video.duration);
    });

    function showMetadata() {
      document.querySelector('#duration').textContent =
        video.duration.toFixed(1);
      seekButton.disabled = false;
    }
    video.addEventListener('loadedmetadata', showMetadata);
    // 元数据若已经就绪,直接更新显示。
    if (video.readyState >= 1) showMetadata();

    video.addEventListener('timeupdate', () => {
      document.querySelector('#time').textContent =
        video.currentTime.toFixed(1);
    });
    video.addEventListener('playing', () => {
      state.textContent = '正在播放';
    });
    video.addEventListener('pause', () => {
      state.textContent = '已暂停';
    });
    video.addEventListener('seeked', () => {
      if (video.paused) {
        state.textContent = '跳转完成,仍处于暂停状态';
      } else {
        state.textContent = '跳转完成,处于播放状态';
      }
    });
    video.addEventListener('ended', () => {
      state.textContent = '播放结束';
    });
  </script>
</body>
</html>