はじめに
WebサイトのKVなどで、PCとSPで異なる動画を出し分けるケースはよくあります。
先日、タグで動画の出し分けを実装したところ、「PCのSafariで閲覧した時だけ、なぜかSP用の動画が読み込まれてしまい、画面いっぱいに拡大・見切れて表示される」という不具合に遭遇しました。
今回は、その原因とパフォーマンス(負荷)にも配慮した解決策を共有します。
失敗した実装(原因)
当初は、画像(タグ)の出し分けと同じ感覚で、以下のようにタグのmedia属性を使って実装していました。
<video class="kv__video js-video" autoplay loop muted playsinline>
<source media="(min-width: 768px)" src="./video/pc.mp4" type="video/mp4">
<source src="./video/sp.mp4" type="video/mp4">
</video>
ChromeやFirefoxでは意図通りにPC/SPの動画が切り替わりますが、Safariの仕様により、タグ内のmedia属性の評価が正しく行われないことがあります。
その結果、メディアクエリが無視されて最初に読み込めた方が再生されてしまい、「PC環境なのにSP用の縦型動画が無理やり横型コンテナで再生され、拡大されたように見える」という現象が起きていました。
解決策:videoタグを2つ用意してCSSとJSで制御する
- HTMLの修正
<source>での切り替えをやめ、独立した<video>タグを用意します。
<video class="kv__video js-video is-pc" autoplay loop muted playsinline>
<source src="./video/pc.mp4" type="video/mp4">
</video>
<video class="kv__video js-video is-sp" autoplay loop muted playsinline>
<source src="./video/sp.mp4" type="video/mp4">
</video>
2.CSSでの表示切り替え
メディアクエリを使用して、画面幅に応じて表示・非表示を切り替えます。
/* デフォルトをPCとする場合 */
.is-sp {
display: none;
}
/* 767px以下(スマホサイズ)の時 */
@media screen and (max-width: 767px) {
.is-pc {
display: none;
}
.is-sp {
display: block;
}
}
3.JavaScriptでの最適化
HTMLとCSSの変更だけでも見た目上の問題は解決しますが、このままだと裏側で非表示になっている動画も再生され続けてしまうという問題が残ります。
そこで、画面に見えている方の動画だけを再生し、見えなくなった方は一時停止するという処理をJavaScriptで追加します。
document.addEventListener('DOMContentLoaded', () => {
const kvVideos = document.querySelectorAll('.js-video');
if (kvVideos.length > 0) {
// 1. 動画が0.1秒進んだらフワッと表示させる処理
// (真っ黒なフレームが一瞬表示されるのを防ぐための対策)
kvVideos.forEach((kvVideo) => {
const checkVideoPlay = () => {
if (kvVideo.currentTime > 0.1) {
kvVideo.classList.add('is-ready'); // CSSで opacity: 1 などを付与する
kvVideo.removeEventListener('timeupdate', checkVideoPlay);
}
};
kvVideo.addEventListener('timeupdate', checkVideoPlay);
});
// 2. リサイズ時に「今見えている方」だけを再生し、隠れた方を停止する関数
const handleVideoPlayOnResize = () => {
kvVideos.forEach((kvVideo) => {
// 現在のCSSスタイルを取得し、画面に表示されているか判定
const style = window.getComputedStyle(kvVideo);
if (style.display !== 'none') {
// 見えているのに止まっていたら再生
if (kvVideo.paused) {
// play()は非同期のため、エラー(自動再生ブロックなど)をcatchで逃がす
kvVideo.play().catch(() => {});
}
} else {
// display: none で裏に回った動画は一時停止(負荷軽減)
if (!kvVideo.paused) {
kvVideo.pause();
}
}
});
};
// 画面幅が変更された時に判定を実行
window.addEventListener('resize', handleVideoPlayOnResize);
// ページ読み込み直後にも初期状態として1回実行しておく
handleVideoPlayOnResize();
}
});
おわり
Safari特有のバグ、焦りますよね…。
同じ現象で困っている方の参考になれば幸いです!