video要素のロード中はローディング表示をしてロード完了後に再生を行う

TIPS 0 Takuya Kobayashi

結論

ソースコードはこちら。
ローディング表示はvideo要素の裏に表示させておくことで対応してください。

<script>
window.addEventListener('DOMContentLoaded', (event) => {
  var video = document.querySelector('video');

  if (video.readyState >= 3) {
    // ビデオの読み込みが既に完了している場合
    document.body.classList.add('loaded');
    video.play();
  } else {
    // ビデオの読み込みが完了するまで 'canplaythrough' イベントを監視
    video.addEventListener('canplaythrough', function() {
      document.body.classList.add('loaded');
      video.play();
    });
  }
});
</script>
<style>
  video {
    transition: .2s opacity;
  }
  body:not(.loaded) video {
    opacity: 0;
  }
  body.loaded video {
    opacity: 1;
  }
</style>
<video src="path/video.mp4" loop muted playsinline></video>

詳細

video要素を背景として使用する場合、次のようなコードを使用すると思います。

<video src="path/video.mp4" autoplay loop muted playsinline></video>

これには次のような問題があります。

  • ロード中は表示されない
  • 十分にバッファされるまでの間、最初のフレームが静止画として表示される

環境によって挙動は異なると思いますが、自分が確認したのが上記です。
ロード中に表示されない件は、video要素の背景にローディングのエフェクトを隠しておけば良いとして、問題なのは二番目のバッファされるまでの間、最初のフレームが静止画として表示される件。
これはautoplay属性がそのような挙動を示すようだったので、autoplay属性ではなくjavascriptで再生の制御をすることにしました。

var video = document.querySelector('video');
// videoの読み込み完了時に発火
video.addEventListener('canplaythrough', function() {
  // bodyにloadedクラスをつける
  document.body.classList.add('loaded');
  // 再生
  video.play();
});

しかし、上記コードではcanplaythroughが発火しない場合がありました。

どうやら、キャッシュを読みこんだ際はcanplaythroughなどのイベントは取得できないようで、
再生が開始されない不具合がありました。そこで次のコードになりました。

<script>
window.addEventListener('DOMContentLoaded', (event) => {
  var video = document.querySelector('video');

  if (video.readyState >= 3) {
    // ビデオの読み込みが既に完了している場合
    document.body.classList.add('loaded');
    video.play();
  } else {
    // ビデオの読み込みが完了するまで 'canplaythrough' イベントを監視
    video.addEventListener('canplaythrough', function() {
      document.body.classList.add('loaded');
      video.play();
    });
  }
});
</script>
<style>
  video {
    transition: .2s opacity;
  }
  body:not(.loaded) video {
    opacity: 0;
  }
  body.loaded video {
    opacity: 1;
  }
</style>

--
以上