JavaScript | Web API:グラフィック・メディア - 動画制御

JavaScript JavaScript
スポンサーリンク

「動画制御」は“ブラウザにあるビデオプレイヤーのリモコンを自分で作る”こと

HTML の <video> タグは、それだけで再生ボタンやシークバーを持っています。
でも JavaScript から操作すると、それを「自分好みのリモコン」で動かせるようになります。

再生・一時停止
巻き戻し・早送り
音量調整・ミュート
特定の時間にジャンプ
再生位置に合わせて UI を更新

こういったことを、全部コードでコントロールできるのが「動画制御」です。
主役になるのは、HTMLVideoElement<video> を JS から触ったもの)です。


まずは最小の「再生・一時停止ボタン」を作ってみる

HTML で video とボタンを用意する

<video id="video" width="400">
  <source src="sample.mp4" type="video/mp4">
  お使いのブラウザは video タグに対応していません。
</video>

<button id="playPauseBtn">再生</button>
<span id="status"></span>

ここでは、ブラウザ標準のコントロールはあえて付けていません。
自分で「再生/一時停止」ボタンを作るためです。

JavaScript で play / pause を呼び分ける

const video = document.querySelector("#video");
const playPauseBtn = document.querySelector("#playPauseBtn");
const status = document.querySelector("#status");

playPauseBtn.addEventListener("click", async () => {
  try {
    if (video.paused) {
      await video.play();
      playPauseBtn.textContent = "一時停止";
      status.textContent = "再生中";
    } else {
      video.pause();
      playPauseBtn.textContent = "再生";
      status.textContent = "一時停止中";
    }
  } catch (err) {
    status.textContent = "再生できませんでした: " + err;
  }
});
JavaScript

ここで押さえてほしいポイントがいくつかあります。

video.paused で「今止まっているかどうか」が分かること。
video.play() は Promise を返すので、awaittry...catch で扱うのが安全なこと。
ボタンのラベルやステータス表示を、状態に合わせて変えていること。

特に play() が非同期なのは重要です。
自動再生制限などで再生がブロックされることがあるので、
失敗を前提に try...catch を書く癖をつけておくと安心です。


再生位置と時間をコントロールする

currentTime で「今どこを再生しているか」を知る・変える

video.currentTime は、動画の「現在の再生位置(秒)」です。

現在位置を表示するだけなら、こう書けます。

setInterval(() => {
  status.textContent = `現在: ${video.currentTime.toFixed(1)} 秒`;
}, 500);
JavaScript

特定の位置にジャンプしたいときは、
currentTime に代入します。

// 10 秒戻る
function rewind10() {
  video.currentTime = Math.max(0, video.currentTime - 10);
}

// 10 秒進む
function forward10() {
  video.currentTime = Math.min(video.duration, video.currentTime + 10);
}
JavaScript

ここでのポイントは、
0 未満や duration(動画の長さ)より大きくならないように
Math.max / Math.min で挟んでいることです。

duration で「動画の長さ」を知る

video.duration は、動画の総再生時間(秒)です。
読み込みが終わるまでは NaN のこともあるので、
loadedmetadata イベントで使うのが安全です。

video.addEventListener("loadedmetadata", () => {
  console.log("動画の長さ:", video.duration, "秒");
});
JavaScript

音量とミュートを制御する

volume と muted を使う

音量は video.volume(0〜1 の数値)で制御できます。

video.volume = 0.5; // 50%
JavaScript

ミュートは video.muted でオン・オフできます。

video.muted = true;  // ミュート
video.muted = false; // ミュート解除
JavaScript

スライダーと組み合わせると、
「自作の音量バー」が作れます。

HTML:

<input type="range" id="volume" min="0" max="1" step="0.05" value="0.5">
<label for="volume">音量</label>

JavaScript:

const volumeSlider = document.querySelector("#volume");

volumeSlider.addEventListener("input", () => {
  video.volume = Number(volumeSlider.value);
});
JavaScript

シークバー付きの簡易プレイヤーを作ってみる

HTML でシークバーを用意する

<video id="video" width="400">
  <source src="sample.mp4" type="video/mp4">
</video>

<div>
  <button id="playPauseBtn">再生</button>
  <input type="range" id="seek" min="0" max="100" value="0">
  <span id="timeLabel">0.0 / 0.0 秒</span>
</div>

JavaScript で「動画 ↔ シークバー」を同期させる

const video = document.querySelector("#video");
const playPauseBtn = document.querySelector("#playPauseBtn");
const seek = document.querySelector("#seek");
const timeLabel = document.querySelector("#timeLabel");

video.addEventListener("loadedmetadata", () => {
  seek.value = 0;
  timeLabel.textContent = `0.0 / ${video.duration.toFixed(1)} 秒`;
});

video.addEventListener("timeupdate", () => {
  if (!isSeeking) {
    const ratio = video.currentTime / video.duration;
    seek.value = String(ratio * 100);
  }
  timeLabel.textContent = `${video.currentTime.toFixed(1)} / ${video.duration.toFixed(1)} 秒`;
});

let isSeeking = false;

seek.addEventListener("input", () => {
  isSeeking = true;
  const ratio = Number(seek.value) / 100;
  video.currentTime = ratio * video.duration;
});

seek.addEventListener("change", () => {
  isSeeking = false;
});

playPauseBtn.addEventListener("click", async () => {
  if (video.paused) {
    await video.play();
    playPauseBtn.textContent = "一時停止";
  } else {
    video.pause();
    playPauseBtn.textContent = "再生";
  }
});
JavaScript

ここで重要なのは、「双方向の同期」です。

動画が進む → timeupdate イベントでシークバーを動かす
シークバーを動かす → currentTime を変えて動画を動かす

さらに、ユーザーがシークバーをドラッグしている間は
timeupdate による上書きを一時的に止めるために
isSeeking フラグを使っています。

こういう「状態の整理」ができるようになると、
動画制御のコードが一気にプロっぽくなります。


イベントを使って「動画の状態に合わせて UI を変える」

よく使うイベント

動画には、状態変化を教えてくれるイベントがたくさんあります。

loadedmetadata … 動画の長さなどの情報が読めるようになった
play … 再生が始まった
pause … 一時停止した
ended … 最後まで再生し終わった
timeupdate … 再生位置が変わった(頻繁に発火)

例えば、最後まで再生し終わったら
「再生」ボタンのラベルを戻す、というのはよくやります。

video.addEventListener("ended", () => {
  playPauseBtn.textContent = "再生";
});
JavaScript

イベントをうまく使うと、
「動画の状態」と「画面の表示」をきれいに同期できます。


初心者として「動画制御」で本当に掴んでほしいこと

動画制御の本質は、
「HTMLVideoElement を、JavaScript からリモコン操作する」 ことです。

そのために、まず次の感覚をしっかり持ってください。

video.play() / video.pause() で再生・一時停止を切り替える。
video.currentTimevideo.duration で「どこを再生しているか」「全体の長さ」を扱う。
video.volumevideo.muted で音量とミュートを制御する。
timeupdateended などのイベントで、UI を動画の状態に合わせて更新する。

おすすめの練習は、次の 2 ステップです。

再生/一時停止ボタンだけのシンプルなプレイヤーを作る。
そこにシークバーと時間表示、音量スライダーを足していく。

一度「自分で作ったプレイヤー」で動画が気持ちよく動く感覚を掴めると、
動画制御はただの API 群ではなく、
「自分のアプリの世界観に合わせて動画体験をデザインするための道具」
として見えてくるはずです。

タイトルとURLをコピーしました