動画ホバー時再生されるまでスタイルを変更する

Shutterstock のように動画サムネイルにホバーした時、スタイルを変更するという実装を最近やったので、そのメモです。

こんな感じの動きを作りたい ↓

前提

一覧ページで video タグがpreload: noneの指定付きで表示されている。この video タグにホバーされた時、動画を再生するようにしたい。
JavaScript で動画のホバー再生・停止を実装するの続きです。

補足:
Shutterstock では最初から video タグを表示させるのではなくて、img タグでサムネイル画像を表示させておき、ホバーされたら video タグを差し込むみたいなことをやっているようでした。

実装方針

  • 動画カードにホバーした時のスタイルを事前に用意しておく
  • ホバーされたかつ、動画の読み込みがまだ完了していないときは、読み込み中のスタイルを適用
  • 動画が再生可能な状態になれば、読み込み中のスタイルを剥がして動画を再生
  • ホバーが外れた時、動画を停止してローディングのスタイルなどが当たっていれば剥がす

動画カードにホバーした時のスタイルを事前に用意しておく

ローディングしている時のスタイルを適用する関数を作ります。Shutterstock のようにサムネイルを若干暗くして、ローディングアイコンを表示させると良いかと思います。

document.querySelectorAll("video").forEach((video) => {
  // ローディングしている時のスタイルを適用する関数
  const styleForLoad = () => {};
});

How To Make a Loader

ホバーされたかつ、動画の読み込みがまだ完了していないときは、読み込み中のスタイルを適用

ホバーされてなおかつ動画の読み込みがまだ完了していないときは、上記の関数を適用します。「動画を読込中か」の判定では、HTMLMediaElement: waiting eventを使うのが良いかとおもいます。

HTMLMediaElement: waiting event - Web APIs | MDN

document.querySelectorAll("video").forEach((video) => {
  // ローディングしている時のスタイルを適用する関数
  const styleForLoad = () => {};
  video.addEventListener("mouseover", () => {
    video.addEventListener("waiting", styleForLoad);
    video.play();
  });
});

動画が再生可能な状態になれば、読み込み中のスタイルを剥がして動画を再生

「動画が再生可能な状態か」の判定では、HTMLMediaElement: canplay eventを使います。

HTMLMediaElement: canplay event - Web APIs | MDN

document.querySelectorAll("video").forEach((video) => {
  // ローディングしている時のスタイルを適用する関数
  const styleForLoad = () => {};
  const styleForPlay = () => {
    // ローディングなどを非表示にする
    video.play(); // 動画の再生を再開
  };
  video.addEventListener("mouseover", () => {
    video.addEventListener("waiting", styleForLoad);
    video.addEventListener("canplay", styleForLoad);
    video.play();
  });
});

ホバーが外れた時、動画を停止してローディングのスタイルなどが当たっていれば剥がす

ホバーが外れたときにはremoveEventListenerを使って、ホバー時に登録したイベントリスナを剥がします。こうしないと、waitingcanplayをトリガーにイベントがずっと走ってしまう(動画の再生がホバーを外しても継続してしまう)ためです。

EventTarget.removeEventListener() - Web APIs | MDN

document.querySelectorAll("video").forEach((video) => {
  // ローディングしている時のスタイルを適用する関数
  const styleForLoad = () => {};
  const styleForPlay = () => {
    // ローディングなどを非表示にする
    video.play(); // 動画の再生を再開
  };
  video.addEventListener("mouseover", () => {
    video.addEventListener("waiting", styleForLoad);
    video.addEventListener("canplay", styleForLoad);
    video.play();
  });

  video.addEventListener("mouseout", () => {
    video.removeEventListener("waiting", styleForLoad);
    video.removeEventListener("canplay", styleForLoad);

    // pauseが非同期のplayを邪魔しないよう、thenの中に入れる
    video.play().then(function () {
      video.pause();
    });
  });
});