HTML video タグで動画埋め込み(背景動画)
video タグの基本的な使い方や mp4 などの動画を背景として表示する方法、スマホでは動画を読み込まないようにしたり、JavaScript を使った動画の操作方法など。
更新日:2023年06月01日
作成日:2023年4月30日
背景動画
以下は HTML の video タグと CSS を使ってコンテンツの背景に動画を表示する例です。
動画を背景に表示するには background は使えないので、動画は video タグで表示し、コンテンツを絶対配置して重ねて表示しています。
Video Background
Lorem ipsum dolor sit amet consectetur adipisicing elit. Error libero, et voluptatem, sit, sint hic eos excepturi vel quo incidunt similique. Quam nemo amet iusto sint facilis dolorem dignissimos quidem.
HTML では動画(video 要素)とコンテンツ(.video-content)を div 要素(.video-wrapper)でラップして、.video-wrapper を配置する基準とします。
video 要素には必要に応じて複数の属性を指定できます。この例では以下の属性を指定しています。
- src:埋め込む動画への URL を指定
- muted:初期状態で消音(ミュート)
- loop:繰り返し(ループ)再生
- playsinline:インライン再生を有効に(iPhone などでインライン表示するために指定)
- autoplay:動画が読み込まれたら自動的に再生
- poster:動画が読み込まれている間に表示する画像を指定
<div class="video-wrapper"> <video src="videos/sample.mp4" muted loop playsinline autoplay poster="images/sample.jpg"></video> <div class="video-content"> <h2>Video Background</h2> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit</p> </div> </div>
上記のように video 要素に直接 src 属性で埋め込む動画を指定することもできますが、以下のように source 要素を使って異なる形式の動画ファイルを複数指定することもできます。
また、video 要素内にはブラウザが video 要素に対応していない場合に表示するメッセージを記述することもできます。
<div class="video-wrapper"> <video muted loop playsinline autoplay poster="images/sample.jpg"> <source src="videos/sample.webm" type="video/webm"> <source src="videos/sample.mp4" type="video/mp4"> <p>動画を再生するには HTML5 video に対応したブラウザが必要です。</p> </video> <div class="video-content"> <h2>Video Background</h2> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit...</p> </div> </div>
CSS では video 要素の親要素(.video-wrapper)に aspect-ratio プロパティ でアスペクト比を指定して、動画を親要素いっぱいに表示しています。
また、::before 疑似要素で動画の上に半透明のオーバーレイを表示しています。
コンテンツは絶対配置して、基本的に水平・垂直方向中央に配置するようにしていますが、top: 43% として垂直方向の位置を調整しています。また、コンテンツ内の全てのテキストを text-align: center で中央寄せにしています。
画面幅が狭い場合(この例では 480px以下の場合)はアスペクト比とコンテンツの位置を調整しています。実際にはフォントサイズなどの調整も必要になるかと思いますが省略しています。
/* ラッパー(外側の要素) */ .video-wrapper { aspect-ratio: 16 / 9; /* 縦横比(アスペクト比) */ overflow: hidden; position: relative; /* 絶対配置するコンテンツや疑似要素の基準とする */ width: 100%; max-width: 1600px; } /* 疑似要素でオーバーレイを表示 */ .video-wrapper::before { content: ""; position: absolute; /* 絶対配置 */ top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(9, 2, 108, 0.15); } /* video 要素 */ .video-wrapper video { width: 100%; /* 明示的に幅を設定(必須) */ height: 100%; /* 明示的に高さを設定(必須) */ object-fit: cover; /* コンテンツボックスに収まるように拡大縮小 */ } .video-content { margin: 0; position: absolute; /* 絶対配置 */ top: 43%; left: 50%; margin-right: -50%; transform: translate(-50%, -50%); color: #fff; text-align: center; } #content .video-content h2 { font-size: 2rem; letter-spacing: 0.1rem; text-shadow: 2px 2px 2px #444; color: #fff; } .video-content p { max-width: 600px; padding: 0 2rem; } @media screen and (max-width: 480px) { .video-wrapper { aspect-ratio: 1 / 1; } .video-content { top:38%; margin-top: 1rem; } }
.video-wrapper { aspect-ratio: 1 / 1; overflow: hidden; position: relative; width: 100%; max-width: 1600px; margin: 0 auto; } .video-wrapper::before { content: ""; position: absolute; top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(9, 2, 108, 0.15); } .video-wrapper video { width: 100%; height: 100%; object-fit: cover; } .video-content { margin: 0; position: absolute; top:38%; margin-top: 1rem; left: 50%; margin-right: -50%; transform: translate(-50%, -50%); color: #fff; text-align: center; } .video-content h2 { font-size: 2rem; letter-spacing: 0.1rem; text-shadow: 2px 2px 2px #444; } .video-content p { max-width: 600px; padding: 0 2rem; } @media screen and (min-width: 481px) { .video-wrapper { aspect-ratio: 1600 / 897; /* 実際の動画に合わせた場合 */ } .video-content { top:43%; margin-top: 0; } } @media only screen and (min-width: 768px){ .video-content h2{ font-size: 2.5rem; } .video-content p { max-width: 600px; padding: 0 0; } } @media only screen and (min-width: 992px){ .video-content h2 { font-size: 3rem; } } @media only screen and (min-width: 1139px){ .video-content h2 { font-size: 3.5rem; } }
スマホでは動画を読み込まない
動画のファイルサイズが大きいと、スマホでは読み込みに時間がかかったり、モバイル通信量を消費するのでスマホでは動画を読み込まず、静止画像を表示するようにする例です。
但し、以下の例の場合、スマホの判定はビューポート幅(480px)を基準にしているので、確実にスマホを判定できるわけではありません。判定方法にはユーザーエージェントなどを使う方法もありますが、推奨される方法ではないので、ビューポート幅で判定しています。
そのため、スマホを横向きでこのページを開くと動画は読み込まれます。
単に CSS で video 要素を非表示にしただけではファイルはダウンロードされてしまうので、この例では JavaScript でメディアクエリを使ってビューポート幅により動画の読み込みを制御しています。
Video Background
Lorem ipsum dolor sit amet consectetur adipisicing elit. Error libero, et voluptatem, sit, sint hic eos excepturi vel quo incidunt similique. Quam nemo amet iusto sint facilis dolorem dignissimos quidem.
HTML の構造は前述の例と同じですが、以下では video 要素に指定する属性は、autoplay を削除し、preload="none" を指定して自動的に動画ファイルを読み込まないようにしています。
<div class="video-wrapper"> <!-- preload="none" を指定し、autoplay は指定しない --> <video muted loop playsinline preload="none" poster="images/sample.jpg"> <source src="videos/sample.webm" type="video/webm"> <source src="videos/sample.mp4" type="video/mp4"> <p>動画を再生するにはvideoタグをサポートしたブラウザが必要です。</p> </video> <div class="video-content"> <h2>Video Background</h2> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit..</p> </div> </div>
ビューポート幅が480px以下の場合は、ビデオを非表示にして背景画像を表示するようにしています。
背景画像を設定しなくても video 要素の poster 属性に指定した画像が表示されますが、この場合は、poster 属性に指定した画像とは異なる画像を表示したいため別途背景画像を用意しています。
また、この例では動画の高さ(video 要素の親要素の高さ)を固定にしています。
.video-wrapper { overflow: hidden; position: relative; width: 100%; max-width: 1600px; height: 500px; /* 高さ固定 */ margin: auto; } .video-wrapper::before { content: ""; position: absolute; top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(0, 0, 0, 0.5); } .video-wrapper video { width: 100%; /* 明示的に幅を設定(必須) */ height: 100%; /* 明示的に高さを設定(必須) */ object-fit: cover; /* ラッパーにに収まるように拡大縮小 */ object-position: 50% 0%; /* 動画の位置の調整 */ } .video-content { margin: 0; position: absolute; top: 43%; left: 50%; margin-right: -50%; transform: translate(-50%, -50%); color: #fff; text-align: center; } .video-content h2 { font-size: 2rem; letter-spacing: 0.1rem; text-shadow: 2px 2px 2px #444; } .video-content p { max-width: 600px; padding: 0 2rem; } /* ビューポート幅が 480px 以下の場合はビデオを非表示にして背景画像を表示 */ @media screen and (max-width: 480px) { .video-wrapper { height: 340px; /* 高さの調整 */ background: url(images/cover.jpg); /* 別途背景画像を用意 */ background-size: cover; background-position: center; background-repeat: no-repeat; } .video-wrapper video { display: none; /* ビデオを非表示(背景画像を表示するため) */ } }
JavaScript では、window.matchMedia() にメディアクエリを指定して作成される MediaQueryList オブジェクトの matches プロパティで指定したメディアクエリの条件にマッチするかを判定しています。
!window.matchMedia('(max-width: 480px)').matches
は、メディアクエリ (max-width: 480px) の条件にマッチしない場合となるので、ビューポート幅が480pxより大きい場合となります。
ビューポート幅が480pxより大きい場合に、video の preload プロパティに auto を設定してファイルを読み込むようにし、autplay を true に設定して自動再生します。
document.addEventListener('DOMContentLoaded', ()=> { // .video-wrapper 内の video 要素を全て取得 const videos = document.querySelectorAll('.video-wrapper video'); // それぞれのビデオ要素について以下を実行 videos.forEach((video) => { // ビューポート幅が 480px より大きい場合はビデオを再生 if (!window.matchMedia('(max-width: 480px)').matches) { // preload プロパティに auto を設定 video.preload = 'auto'; // autplay プロパティを true に設定して自動再生 video.autoplay = true; } }); }, false);
以下は上記をモバイルファーストに書き換えた例です(HTML は同じです)。この場合は、ビューポート幅が480px未満の場合は、ビデオを非表示にして背景画像を表示するようにしています。
.video-wrapper { overflow: hidden; position: relative; width: 100%; height: 340px; background: url(images/cover.jpg); /* 別途背景画像を用意 */ background-size: cover; background-position: center; background-repeat: no-repeat; } .video-wrapper::before { content: ""; position: absolute; top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(0, 0, 0, 0.5); } .video-wrapper video { width: 100%; height: 100%; object-fit: cover; object-position: 50% 0%; display: none; /* ビデオを非表示(背景画像を表示するため) */ } .video-content { margin: 0; position: absolute; top: 43%; left: 50%; margin-right: -50%; transform: translate(-50%, -50%); color: #fff; text-align: center; } .video-content h2 { font-size: 2rem; letter-spacing: 0.1rem; text-shadow: 2px 2px 2px #444; } .video-content p { max-width: 600px; padding: 0 2rem; } /* 480px 以上の場合はビデオを表示 */ @media screen and (min-width: 480px) { .video-wrapper { height: 500px; max-width: 1600px; margin: auto; background: none; } .video-wrapper video { display: block; /* ビデオを表示 */ } }
document.addEventListener('DOMContentLoaded', () => { const videos = document.querySelectorAll('.video-wrapper video'); videos.forEach((video) => { // ビューポート幅が 480px 以上の場合はビデオを再生 if (window.matchMedia('(min-width: 480px)').matches) { video.preload = 'auto'; video.autoplay = true; } }); }, false);
ローディングアイコンを表示
動画のファイルサイズが大きい場合、通信環境によっては読み込み時間が長くなってしまうので、動画の読み込み中(正確には動画が再生できる状態になるまで)はローディングアイコンを表示する例です。
以下のサンプルの場合、動画のファイルサイズは 3.5MB なので LAN 環境ではローディングアイコンはほとんど見えないかと思います。
Video Background
Lorem ipsum dolor sit amet consectetur adipisicing elit. Error libero, et voluptatem, sit, sint hic eos excepturi vel quo incidunt similique. Quam nemo amet iusto sint facilis dolorem dignissimos quidem.
サンプルを別ウィンドウで開く(ビデオサイズ:7.5MB)
HTML は前述の例と同様、初期状態では video 要素に preload="none" を指定して動画を読み込まないようにして、JavaScript でビューポート幅が480px以上の場合は動画を読み込み再生するようにしています。
<div class="video-wrapper"> <video muted loop playsinline preload="none" poster="images/sample.jpg"> <source src="videos/sample.webm" type="video/webm"> <source src="videos/sample.mp4" type="video/mp4"> <p>動画を再生するにはvideoタグをサポートしたブラウザが必要です。</p> </video> <div class="video-content"> <h2>Video Background</h2> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit...</p> </div> </div>
JavaScript では各 video 要素に canplay イベントのリスナを設定し、動画が再生できる状態になったこと(canplay イベント)を検知したら video_ready というクラスを追加します。
document.addEventListener('DOMContentLoaded', () => { // .video-wrappe を取得 const videoWrappers = document.querySelectorAll('.video-wrapper'); // メディアクエリを設定して MediaQueryList オブジェクトを作成 const mql = window.matchMedia('(min-width: 480px)'); // 取得した各 .video-wrappe で videoWrappers.forEach((wrapper) => { // video 要素を取得 const video = wrapper.querySelector('video'); // video 要素に canplay イベントのリスナを設定 video.addEventListener('canplay', () => { // 動画が再生できる状態になったら video_ready クラスを追加 wrapper.classList.add('video_ready'); }, false); //ビューポート幅が480px以上の場合は動画を再生 if (mql.matches) { video.preload = 'auto'; video.autoplay = true; } else { //ビューポート幅が480px未満ではvideo_readyクラスを追加(ローディング画像を表示しない) video.parentElement.classList.add('video_ready'); } }); }, false);
ビデオが再生できる状態になったら、JavaScript により .video-wrapper に .video_ready をいうクラスが追加されるので、それを利用してローディングアイコンの表示・非表示やスタイルを設定します。
ローディング画像は、.video-wrapper に .video_ready クラスが追加されるまでの間 ::after で表示します。また、.video-wrapper の背景画像に poster 属性に設定する画像を指定して背景画像を表示し、JavaScript でビューポート幅が480px以上の場合は背景画像を削除して代わりに動画を表示します。
.video-wrapper { aspect-ratio: 16 / 9; overflow: hidden; position: relative; width: 100%; background: url(images/sample.jpg); /* poster 属性に設定する画像を指定 */ background-size: cover; background-position: center; background-repeat: no-repeat; } .video-wrapper::before { content: ""; position: absolute; top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(255, 255, 255, 0.7); transition: background-color 1s; /*オーバーレイにトランジションを設定 */ } /* JavaScript で .video_ready が付与された際のオーバーレイ */ .video-wrapper.video_ready::before { background-color: rgba(0, 0, 0, 0.3); } /* JavaScript で .video_ready が付与されるまではローディング画像を表示 */ .video-wrapper:not(.video_ready)::after { content: ""; position: absolute; top: 50%; left: 50%; transform: translateX(-50%) translateY(-50%); width: 80px; height: 80px; background-image: url(images/spinner.png); /* ローディング画像 */ background-repeat: no-repeat; background-position: center; background-size: contain; } /* ビューポート幅が480px未満の場合はビデオを非表示 */ .video-wrapper video { width: 100%; height: 100%; object-fit: cover; display: none; /* ビデオを非表示 */ } /* ビューポート幅が480px以上の場合 */ @media screen and (min-width: 480px) { /* .video_ready が付与されたら背景画像を非表示 */ .video-wrapper.video_ready { background: none; } /* .video_ready が付与されたらビデオを表示 */ .video-wrapper.video_ready video { display: block; /* ビデオを表示 */ } /* .video_ready が付与された場合はトランジションの長さを変更 */ .video-wrapper.video_ready::before { transition: background-color 8s; } } .video-content { margin: 0; position: absolute; top: 43%; left: 50%; margin-right: -50%; transform: translate(-50%, -50%); color: #fff; text-align: center; } .video-content h2 { font-size: 2rem; letter-spacing: 0.1rem; text-shadow: 2px 2px 2px #111; } .video-content p { max-width: 600px; padding: 0 2rem; }
別ウィンドウで開くサンプルでは、.video-wrapper のスタイルを以下のように設定してビデオを全画面表示するようにしています。
.video-wrapper { width: 100%; height: 100vh; overflow: hidden; position: relative; background: url(images/sample.jpg); background-size: cover; background-position: center; background-repeat: no-repeat; }
この例で使用しているローディングアイコン(以下)は icons8.com で作成したものです。
好きなアイコンを選択すると以下のようなウィンドウが開くので、ファイルのタイプや背景を透過にするか、サイズや色などが編集できるようになっています。編集したら「Generate」をクリックすると右側にプレビューが表示されるので問題なければ Download をクリックします。
また、画像を使わない以下のような HTML と CSS によるローディングアイコンもあります。
以下は Spinkit に掲載されているローディングアイコンを使った例です。
HTML と CSS ではサイトからコピーしたローディングアイコンの HTML と CSS を追加しています。JavaScript は前述の例と同じです。
<div class="video-wrapper"> <video muted loop playsinline preload="none" poster="images/sample.jpg"> <source src="videos/sample.webm" type="video/webm"> <source src="videos/sample.mp4" type="video/mp4"> <p>動画を再生するにはvideoタグをサポートしたブラウザが必要です。</p> </video> <div class="spinner"> <!-- ローディングアイコンの HTML --> <div class="bounce1"></div> <div class="bounce2"></div> <div class="bounce3"></div> </div> <div class="video-content"> <h2>Video Background</h2> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit...</p> </div> </div>
このサンプルの場合、LAN 環境などではすぐに再生可能になり、ローディングアイコンの表示がわからないため、再生可能になってからトランジションで2秒かけてアイコンを非表示にしていますが、実際に使用する際は即座に非表示にすることになるかと思います。
サンプルを別ウィンドウで開く(ビデオサイズ:7.5MB)
video 要素(タグ)
HTML の video 要素(タグ)を使って動画(ファイル)を表示する(埋め込む)ことができます。
表示可能な動画のファイル形式はブラウザにより異なりますが、最も広くサポートされているのは MP4(拡張子は .mp4 コーデックは h.264)になります。
主要なモダンブラウザでは .mp4 以外にも .webm や .mov もサポートしています。
動画関連参考サイト(MDN)
- <video>: 動画埋め込み要素
- 動画と音声のコンテンツ
- HTMLMediaElement
- Video player styling basics
- Creating a cross-browser video player
video タグを使って動画ファイルを埋め込むには src 属性に動画ファイルのパスを指定します。
<video src="sample.mp4"></video><!-- src 属性に動画ファイルを指定 -->
上記を記述すると src 属性に指定した動画が表示されます。
※ 但し、iPhone などでは src 属性を指定しただけでは何も表示されず、空白になっています。
src 属性だけを指定しただけでは再生ボタンなどは表示されず、自動的に再生もされません。
video タグには src 属性の他に動画コントロール(再生ボタンなど)を表示するかや自動再生やループをするか、動画の幅や高さなどの属性を指定することができます。
video 要素の属性
video 要素にはグローバル属性(すべての HTML 要素で共通の属性)の他に以下のような属性を指定することができます。
属性 | 説明 |
---|---|
src | 埋め込む動画への URL を指定します。この属性を省略して video 要素内で source タグを使用して埋め込む動画を指定することもできます。 |
controls | この属性を指定すると、再生、音量、シーク、ポーズの各機能を制御するコントロールを表示します。表示されるコントロールはブラウザにより異なります。 |
controlslist | この属性を指定すると表示するコントロールを選択することができ、以下を指定できます。(例) controlslist="nodownload"(ダウンロードメニューを非表示)
|
autoplay | この属性を指定すると、データの読み込みが完了し、再生可能な状態になった時点で即座にコンテンツの再生が始まります。Chrome などのブラウザでは自動再生するには muted 属性も同時に指定する必要があります。 |
muted | この属性を指定すると、初期状態でミュート(音が消された状態)になります。 |
playsinline | 動画のインライン再生を有効にします。インライン再生とは、全画面表示せずにその位置で再生させることです。playsinline 属性が指定されていないと、iPhone などでは再生ボタンをタップすると拡大画面が表示されてそこで動画が再生されます。 |
loop | この属性を指定すると、動画を繰り返し(ループ)再生します(動画の末尾に達すると自動的に先頭に戻って繰り返し再生)。 |
poster | 動画が読み込まれている間(ダウンロード中)に表示する画像を指定することができます。この属性が指定されない場合、最初のフレームが利用可能になるまで何も表示されず、その後、最初のフレームをポスターフレームとして表示します。 |
preload | 動画ファイルを事前に読み込むかどうかを指定する属性で次の値を指定できます。
|
width / height | 動画の表示領域の幅と高さをピクセル値で指定します(単位は付けません)。表示領域の幅と高さを設定できますが、動画自体の縦横比は変更できません。画像(img 要素)同様、レイアウトシフト対策として、width と height を指定することが推奨されます(または CSS で aspect-ratio を指定)。 |
以下の動画のサンプルには次のようなスタイルを設定しています。
video { aspect-ratio: 16/9; width: 70%; height: auto; max-width: 500px; }
コントロールの表示(controls 属性)
再生ボタンなどのコントロールを表示するには video 要素に controls を指定します。
但し、表示されるコントロール(ボタンなどの形状や何が表示されるか)はブラウザにより異なります。
以下では controls 属性の他に、playsinline 属性と poster 属性も指定しています。
<video src="sample.mp4" controls playsinline poster="poster.jpg"></video>
playsinline 属性(iPhone)
playsinline 属性は iPhone や iPad でインライン再生できるようにするために指定します。
playsinline 属性が指定されていないと、iPhone などでは再生ボタンをタップすると拡大画面が表示されて動画が再生されます。
また、iPhone では自動再生しない場合、背景に何も表示されないので、以下では合わせて poster 属性も指定して背景に代替画像が表示されるようにしています。
<video src="sample.mp4" controls playsinline poster="poster.jpg"></video>
preload 属性
preload 属性は動画ファイルを事前に読み込むかどうかを指定する属性で次の値を指定できます。
- none: 事前に動画ファイルを読み込まない。
- metadata: 動画のメタデータ (再生時間などのメタ情報) のみを読み込む。
- auto: 事前に動画ファイルを読み込む。
- 空文字列: auto を指定したのと同じ。
既定値はブラウザーごとに異なるため必要に応じて明示的に指定します。
また、autoplay 属性を指定している場合は、preload="none" を指定しても事前に動画ファイルが読み込まれます(autoplay 属性は preload 属性より優先されます) 。
preload="none"
以下は preload="none" を指定して、事前に動画ファイルを読み込まないようにする例です。
preload="none" を指定した場合、controls 属性や poster 属性に代替画像を指定していないと何も表示されません(動画のスペース分の空白)。
controls 属性を指定して poster 属性を指定していない場合はコントロールのみが表示されますが、ブラウザにより表示が異なります(Chrome では背景が黒、Firefox や Safari では透明)。
<video src="sample.mp4" controls playsinline preload="none" poster="poster.jpg"></video>
この例では controls 属性と poster 属性に動画の最初のフレームと同じ画像を指定しています。
※ preload 属性に metadata を指定すれば、動画のメタデータのみが読み込まれ、最初のフレームをポスターフレーム(ポスター画像)として表示します。但し、Safari ではポスター画像が表示されないようなので、確実に何らかの画像を表示するには poster 属性に代替画像を指定します。
また、preload="none" を指定した場合、実際にファイルが読み込まれるまでブラウザは画像ファイルのサイズがわからないので、width 属性と height 属性を指定するか、CSS で幅と高さを指定します。
poster 属性(代替画像の表示)
poster 属性を使って動画ファイルが読み込まれている間に表示する画像を指定することができます。
この属性が指定されない場合、最初のフレームが利用可能になるまで何も表示されず、その後、最初のフレームをポスターフレームとして表示します。
今までの例では poster 属性に最初のフレームと同じ画像を使用していますが、以下は文字を入れた画像を指定しています。
以下は preload="none" を指定しているので、画像は読み込まれず最初のフレームも表示されませんが、poster 属性で指定した画像が表示されます。再生ボタンをクリックすると動画に切り替わります。
autoplay 属性(及び muted 属性)を指定した場合は、動画ファイルが読み込まれている間は poster 属性で指定した画像が表示され、その後動画が自動再生され表示されます。
<video src="sample.mp4" controls playsinline preload="none" poster="poster2.jpg"></video>
自動再生(autoplay 属性と muted 属性)
autoplay 属性を指定すると、データの読み込みが完了し、再生可能な状態になった時点で即座にコンテンツの再生が始まります。
但し、Chrome などでは muted 属性が指定されていない場合 autoplay は動作しないので、自動再生するには muted 属性も一緒に指定します。また、ページを表示していきなり音が鳴るのは好ましくないので、通常自動再生する場合は muted 属性も指定します
以下は autoplay、muted、playsinline、loop、poster 属性を指定して自動再生する例です。
poster 属性で代替画像を指定しているので、動画がダウンロードされるまでは代替画像が表示され、再生可能になると自動的に再生が始まり、loop 属性を指定しているので繰り返し再生されます。
controls 属性を指定していないので、再生ボタンなどのコントロールは表示されませんが、右クリックでコンテキストメニューからコントロールを表示することができます。
<video src="sample.mp4" autoplay muted playsinline loop poster="poster2.jpg"></video>
ダウンロードの防止
Chrome などでは controlslist 属性の値に nodownload を指定すると「ダウンロード」メニューを表示しないようにすることができます。但し、controlslist 属性は一部のブラウザでサポートされていません。
以下のように controlslist="nodownload" を指定すると、Chrome では右端のアイコンをクリックして表示されるメニュー項目から「ダウンロード」が消えますが、動画上で右クリックすると「名前を付けて動画を保存」が表示され、ダウンロードすることは可能です。
<video src="sample.mp4" controlslist="nodownload" controls playsinline poster="poster.jpg"></video>
但し、controlslist 属性は実験的なものであり、すべてのブラウザーでサポートされているとは限らないため、W3C の Markup Validation Service で検証すると「Error: Attribute controlslist not allowed on element video at this point.」のようなエラーになります。JavaScript のプロパティで設定することもできます。
コンテクストメニューを表示させない
oncontextmenu="return false;" を指定することで、右クリックでのコンテクストメニューを表示しないようにすることができます。
<video src="sample.mp4" controlslist="nodownload" oncontextmenu="return false;" controls playsinline poster="poster.jpg">></video>
上記は JavaScript を使って設定することもできます。
//全てのビデオ要素を取得 const videos = document.querySelectorAll('video'); //ビデオ要素が1つ以上あれば if(videos.length > 0) { videos.forEach(function(elem) { //ダウンロードメニューを表示しない elem.setAttribute('controlslist', 'nodownload'); //コンテクストメニューを表示しない elem.addEventListener('contextmenu', (e) => { e.preventDefault(); }); }); }
完全にはダウンロードは防げない
但し、HTML のソースコードを見てビデオの URL からアクセスできてしまいます。
以下を .htaccess に記述することで、ビデオファイル(mp4、webm)に自分のサイト(以下の場合は https://www.example.com)からのみアクセスを許可できますが、リファラ(Referer)は偽装も可能なため、完全にアクセス(ダウンロード)を防ぐことはできません。
SetEnvIf Referer "^https://www\.example\.com" mysite <Files ~ "\.(mp4|webm)$"> order deny,allow deny from all allow from env=mysite </Files>
代替コンテンツ
video タグ内にテキストを記述すると、ブラウザが video 要素に対応していない場合にその内容が表示されます。ブラウザが video 要素に対応していれば、動画が表示されてテキストは表示されません。
テキストは p 要素などを使っても、単にテキストだけ記述しても問題ありません。
<video src="sample.mp4" controls> <p>お使いのブラウザーは HTML5 動画をサポートしていません。</p> </video>
※ 現在のほどんどのブラウザは video 要素に対応しているため、mp4 形式(コーデック h.264)の動画であれば、代替コンテンツを記述しなくとも問題ないと思います。
代替画像(背景画像)
代替画像は video 要素の poster 属性で指定することができます。そのため、敢えて設定する必要はないと思いますが video 要素に background プロパティを使って背景画像を指定することもできます。
但し、poster 属性で指定した画像は preload="none" を指定した場合に表示されますが、 background で指定した画像は(video 要素に対応しているブラウザでは)表示されないので、 background プロパティを指定する場合でも poster 属性を指定した方が確実です。
サイズの指定
width 属性と height 属性を使って動画の表示領域の幅と高さをピクセル値で指定することができますが固定値になります。以下の場合、幅320px、高さ180pxの固定サイズで表示されます。
video 要素の属性は省略しています。実際には controls playsinline 及び poster を指定しています。
<video src="sample.mp4" width="320" height="180"></video>
以下は width 属性と height 属性に実際の画像ファイルのサイズを指定し、CSS で表示サイズを設定する例です。img 要素同様、width 属性と height 属性を指定することで、レイアウトシフトを防止することができます(controls 属性などの記述は省略しています)。
レイアウトシフトの防止が目的の場合、width 属性と height 属性では縦横比が示せれば良いので、この場合、width="128" height="72" や width="16" height="9" でも問題ありませんが、CSS でサイズを指定しないとその値で表示されてしまいます。
<video class="foo" src="sample.mp4" width="1280" height="720"></video>
video 要素に width 属性と height 属性を指定してレスポンシブにする場合、CSS では width を%で指定し、height には auto を指定します。CSS での height を省略すると、height 属性で指定した高さ(この場合は 720px)が確保されてしまいます。
.foo { width: 70%; height: auto; /* auto を指定 */ }
以下は video 要素を div 要素(.video-wrapper)でラップする例です。
.video-wrapper に width と max-width で幅と最大幅を指定して、video 要素は親要素の .video-wrapper の幅いっぱいに表示するようにしています(controls 属性などの記述は省略しています)。
<div class="video-wrapper"> <video src="sample.mp4" width="1280" height="720"></video> </div>
この例の場合も、video 要素に width 属性と height 属性を指定しているので、CSS では video 要素に height: auto を指定します。
.video-wrapper { width: 70%; /* % で指定して可変に */ max-width: 500px; /* 必要に応じて最大幅を指定 */ } .video-wrapper video { width: 100%; /* 親要素の幅いっぱいに */ height: auto; /* height には auto を指定 */ }
縦横比(aspect-ratio)
aspect-ratio プロパティを使って要素にアスペクト比(縦横比)を設定することができます。
以下は video 要素に aspect-ratio を使って縦横比を設定する例です。
この例の場合、表示する動画ファイルは width="1280" height="720" なのでアスペクト比は 1280:720 (16:9)になり、以下のように指定することができます。
aspect-ratio を設定する場合、幅または高さのどちらかを指定します(他方は自動的に決定されますが、他方は auto を指定したほうが安全です)。
.asp16x9 { aspect-ratio: 16/9; /* アスペクト比を設定(1280/720 でも同じ) */ width: 70%; /* % で指定(可変幅) */ height: auto; /* height には auto を指定 */ max-width: 500px; /* 必要に応じて */ }
aspect-ratio プロパティを使ってアスペクト比を設定することでレイアウトシフトを防止できるので、video 要素の width 属性と height 属性の指定は省略できます。
※ 上記の場合、CSS で height: auto; を指定ぜずに video 要素に height 属性を指定すると、その値が有効になってしまいます。
<video class="asp16x9" src="sample.mp4" controls playsinline></video>
以下はアスペクト比を指定した div 要素で video 要素をラップして配置する場合の例です。
<div class="video-wrapper"> <video src="sample.mp4" controls playsinline></video> </div>
表示する動画と同じ(または表示したい)アスペクト比と幅を設定した div 要素に overflow: hidden を指定します。
親要素の div 要素に video 要素がフィットするように、 video 要素の width と height に 100% を指定して object-fit: cover を指定します。
.video-wrapper { aspect-ratio: 16/9; /* 親要素に動画と同じ(または表示したい)アスペクト比を設定 */ width: 70%; /* % で指定して可変幅に */ max-width: 500px; /* 必要に応じて最大幅を指定 */ overflow: hidden; /* 指定したアスペクト比からはみ出さないように */ } .video-wrapper video { width: 100%; /* 親要素の幅いっぱいに表示 */ height: 100%; /* 親要素の幅いっぱいに表示 */ object-fit: cover; /* コンテンツボックスに収まるように拡大縮小 */ }
padding ハック
以下の padding ハックでも上記と同じ結果になります。padding ハックは aspect-ratio プロパティが広くサポートするまで使用されいていた手法で、コードが分かりにくいのと長くなるデメリットがあります。
.video-wrapper { position: relative; /* 子要素(video)を配置する基準とする */ overflow: hidden; width: 70%; /* 上記の例と同じになるように 70% に指定 */ max-width: 500px; /* 上記の例と同じになるように指定 */ } .video-wrapper::before { content: ""; /* 空文字列を指定 */ display: block; /* display: block でブロック要素に */ padding-top: calc(9 / 16 * 100%) ; /* 56.25% でも同じ*/ } .video-wrapper video { position: absolute; /* 絶対配置 */ top: 0; left: 0; width: 100%; /* 幅 100% */ height: 100%; /* 高さ 100% */ }
異なる縦横比で表示
aspect-ratio と object-fit を使えば、実際の動画ファイルの縦横比とは異なる縦横比で(トリミングして縦横比を維持したまま)表示することができます。
以下は縦横比が 16:9 の動画を縦横比 3:1 で表示する例です。
<div class="video-wrapper"> <video src="sample.mp4" controls playsinline></video> </div>
親要素に縦横比(アスペクト比)を指定し、video 要素には width: 100% と height: 100% を指定し、object-fit: cover を指定することで親要素のコンテンツボックスに収まるように拡大縮小(トリミング)されて表示されます。
親要素にフィットするように object-fit を設定する際は、width と height の両方に 100% を指定します。
また、表示する video 要素の位置は object-position で調整することができます。この例では垂直方向トップ(0%)にしています。
.video-wrapper { aspect-ratio: 3/1; /* 動画ファイルとは異なる縦横比を指定 */ width: 70%; /* % で指定(可変幅) */ max-width: 500px; /* 必要に応じて */ } .video-wrapper video { width: 100%; /* 親要素の幅いっぱいに表示 */ height: 100%; /* 親要素の高さいっぱいに表示 */ object-fit: cover; /* コンテンツボックスに収まるように拡大縮小 */ object-position: 50% 0%; /* 位置の調整 */ }
以下は video 要素自体に aspect-ratio と object-fit を指定する例です。
<video class="asp3x1" src="sample.mp4" controls playsinline></video>
aspect-ratio を設定した要素(この場合は video 要素)には、width または height のいずれかを指定します(他方は自動的に算出されます)。
.asp3x1 { aspect-ratio: 3/1; width: 70%; /* % で指定(可変幅) */ max-width: 500px; /* 必要に応じて */ object-fit: cover; object-position: 50% 0%; }
可変幅で高さを固定
以下は高さ固定で可変幅の親要素に video 要素を配置する例です。
video 要素には、width: 100% と height: 100%、及び object-fit: cover を指定します。必要に応じて object-position で位置を調整します。
<div class="video-wrapper"> <video src="sample.mp4" controls playsinline></video> </div>
.video-wrapper4 { width: 80%; /* 可変幅 */ height: 200px; /* 高さ固定 */ } .video-wrapper4 video { width: 100%; /* 親要素の幅いっぱいに表示 */ height: 100%; /* 親要素の高さいっぱいに表示 */ object-fit: cover; /* コンテンツボックスに収まるように拡大縮小 */ object-position: 50% 0%; /* 位置の調整 */ }
以下は video 要素自体を高さ固定で可変幅で表示する例です。
<video class="fixed-height" src="sample.mp4" controls playsinline></video>
.fixed-height { width: 80%; height: 200px; object-fit: cover; object-position: 50% 0%; }
複数の形式の動画を指定して読み込む
video 要素の中で source タグ(要素)を使って、複数の異なる形式の動画ファイルを指定できます。
ブラウザーやデバイスにより動画ファイル形式の対応状況が異なる可能性があるので、複数のファイル形式を準備しておき、ブラウザが自動的に最適なファイル形式を選択できるようにすることができます。
source 要素
source 要素は video 要素の入れ子として複数指定できる要素で、各 source 要素の src 属性に動画のデータと type 属性にファイルの形式を指定できます。
source 要素は空要素なので、閉じタグ </source> はありません。
以下の場合、最初に記述した WebM を試し、再生できない場合は MP4 を試します。 video 要素に対応していない場合は代替メッセージを表示します。
<video controls playsinline > <source src="sample.webm" type="video/webm"> <!-- WebM 形式 --> <source src="sample.mp4" type="video/mp4">; <!-- MP4 形式 --> お使いのブラウザーは HTML5 の video タグに対応していません。 </video>
WebM は HTML5 のために作成された Google が提供している動画ファイル形式で、ブラウザ単体で再生することができます。また、MP4 ファイルよりも圧縮効率が良いためファイルを軽くできます。
type 属性
type 属性が指定された場合、ユーザーエージェントが表示できる形式と比較し、扱えないものであれば、次に記述されている source 要素をチェックします。
type 属性が指定されていない場合は、サーバーからメディア形式を取得して、ユーザーエージェントが扱うことができるかどうかを確認し、表示ができない場合は、次の source 要素をチェックします。
また、type 属性にはオプションで codecs 引数を指定することもできます。
MDN:ウェブ動画コーデックガイド
Javascript で video を操作
JavaScript を使って動画(video 要素)を操作することができます。
以下は各プロパティで必要な属性を設定し、play() メソッドを使ってビデオを再生する例です。
属性は直接 HTML に記述することもできますし、以下のようにプロパティを使って設定することもできます。但し、playsinline というプロパティはないので、setAttribute() で追加しています。
<video class="js-video"></video>
video 要素を操作する際は基本的には DOMContentLoaded を利用して問題ないと思います。
document.addEventListener('DOMContentLoaded', () => { const video = document.querySelector('.js-video'); // 対象の video 要素を取得 video.src = "sample.mp4"; //src プロパティに動画ファイルの URL を設定 video.muted = true; //muted プロパティに true を設定 video.controls = true; //controls プロパティに true を設定 video.poster="poster.jpg" //poster プロパティに代替画像を設定 video.width = "640"; //width プロパティで幅を設定 video.height = "360"; //height プロパティで高さを設定 video.setAttribute('playsinline',''); //setAttribute()で playsinline 属性を設定 video.play(); //play() メソッドで再生 (または video.autoplay = true; で自動再生) });
play() メソッドの代わりに autoplay プロパティに true を設定することで自動再生させることもできます。
また、muted プロパティに true を設定しない(または muted 属性を指定しない)で再生しようとすると、Safari や Firefox ではエラーになりませんが、Chrome では以下のようなエラーになります。
caught (in promise) DOMException: play() failed because the user didn't interact with the document first.
HTMLMediaElement / HTMLVideoElement
video 要素(及び audio要素)は HTMLMediaElement インターフェイスを継承していて、そのプロパティやメソッド、イベントを使うことができます。
また、video 要素は HTMLMediaElement から派生した HTMLVideoElement インターフェースによって実装されていて、video 要素を制御するためのメソッドやプロパティ、イベントが用意されています。
video のプロパティ
video 要素では以下のような HTMLMediaElement のプロパティを利用できます。
プロパティ | 説明 |
---|---|
autoplay | HTML の autoplay 属性の値を反映し、論理値で指定します。初期値:false |
controls | HTML の controls 属性を反映し、コントロールを表示するかどうかを論理値で指定します。初期値:false |
controlsList | HTML の controlslist 属性に該当します(HTML の属性の list の l は小文字で、プロパティは大文字の L )。nodownload、nofullscreen、noremoteplayback を含む DOMTokenList を返します。読み取り専用。Chrome などでは video.controlsList = 'nodownload nofullscreen' のように設定できますが、「読み取り専用」とあるので setAttribute() を使った方が良いのかも知れません。 |
currentTime | 現在の再生時刻を秒単位で示します。値を設定・変更すると指定された時刻にシーク(移動)します。(例)video.currentTime = 5
iPhone ではロード前に currentTime に0以外の値を設定すると正常に動作しません。currentTime に0以外の値を設定する場合は、preload に metadata を設定するか、loadeddata イベントを使って設定します(詳細)。 |
duration | 再生時間を秒単位で示す倍精度浮動小数点値(読み取り専用)。メディアデータがない(指定されたデータが見つからなかったり、まだ、読み込まれていない)場合は NaN を返し、ライブメディアストリームなど再生時間が不明な場合は +Infinity を返します。メタ情報の読み込みが完了したタイミング(loadedmetadata イベント)で取得すると良いようです。 |
ended | 再生を終了したかどうかを示す真偽値を返します(読み取り専用)。再生が終了した場合に true になります。 |
error | 最新のエラーの MediaError オブジェクトです(読み取り専用)。エラーが発生していない場合は null。要素が error イベントを受け取ったら、このプロパティを調べることでエラーの詳細を調べられます。 |
loop | 繰り返し(ループ)再生するかどうかを論理値で指定します。初期値:false |
muted | 音声がミュートされているかどうかを論理値で指定します。初期値:false |
paused | ビデオが一時停止中であるか否かを論理値で返します(読み取り専用)。 |
poster | 代替画像の URL を指定します。 |
playbackRate | メディアが再生されるレートを設定します。(例)video.playbackRate = 1.5 |
preload | 動画ファイルを事前に読み込むかどうかを設定します。none, metadata, auto のいずれかを指定できます。 |
readyState | メディアの準備状態を示すプロパティです(読み取り専用)。以下が定数とその値。
|
src | 再生する動画への URL を指定(参照)します。 |
volume | 音声の音量を double(無音 0.0 から最大 1.0 の間)で指定(参照)します。 |
video 要素では HTMLVideoElementt のプロパティやメソッドなども利用できます。
以下は、setAttribute() で controlslist 属性を追加し、contextmenu イベントでコンテクストメニュー(右クリックで表示されるメニュー)を非表示にする例です。
controlslist 属性は controlsList プロパティ(L は大文字)を使って video.controlsList = 'nodownload nofullscreen' のようにも設定できますが、MDN によると controlslist プロパティは読み取り専用となっているので、setAttribute() を使った方が良いのかも知れません。
document.addEventListener('DOMContentLoaded', () => { const video = document.querySelector('.js-video'); video.src = "sample.mp4"; video.muted = true; video.controls = true; video.poster="poster.jpg" video.width = "640"; video.height = "360"; video.setAttribute('playsinline',''); // ダウンロードメニューを表示させない video.setAttribute('controlslist', 'nodownload'); // フルスクリーン表示させない(Safari では無視される) video.setAttribute('controlslist', 'nofullscreen'); // 上記は video.controlsList = 'nodownload nofullscreen'; でも同じ // コンテクストメニューを表示しない video.addEventListener('contextmenu', e => { e.preventDefault(); }); video.autoplay = true; //自動再生 または video.play(); });
音声のオン・オフ
autoplay 属性を指定する場合は、muted 属性を指定して初期状態として無音にする必要があります。
コントロールを表示してミュートをユーザーに解除してもらうこともできますが、以下はチェックボックスを表示して音声のオン・オフを操作できるようにする例です。
HTML では input 要素を使ってチェックボックスを表示します。
<div class="video-wrapper"> <video poster="poster.jpg" muted loop playsinline autoplay> <source src="sample.webm" type="video/webm"> <source src="sample.mp4" type="video/mp4"> <p>動画を再生するにはvideoタグをサポートしたブラウザが必要です。</p> </video> <div class="video-content"> <h2>Video Background</h2> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p> </div> <div class="bgm-opt"> <input type="checkbox" id="music-on1"> <!-- チェックボックス --> <label for="music-on1"> 音声 </label> </div> <p class="video-credit">video by fotogen</p> </div>
JavaScript ではチェックボックスの change イベントを使って、チェックされれば muted 属性を false にして音を出すようにし、外されれば muted 属性を true にして消音状態にします。
document.addEventListener('DOMContentLoaded', () => { const videoWrappers = document.querySelectorAll('.video-wrapper'); videoWrappers.forEach((wrapper) => { const video = wrapper.querySelector('video'); // 必要に応じてボリュームを調整 video.volume = .3; // チェックボックスの要素 const checkbox = wrapper.querySelector('input[type="checkbox"]'); // チェックボックスの change イベントにリスナーを設定 checkbox.addEventListener('change', (e) => { if (e.currentTarget.checked) { // チェックされれば muted 属性を false に(音を出す) video.muted = false; } else { video.muted = true; } }); }); }, false);
.video-wrapper { aspect-ratio: 1 / 1; overflow: hidden; position: relative; width: 100%; max-width: 1600px; margin: 0 auto; } .video-wrapper::before { content: ""; position: absolute; top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(9, 2, 108, 0.15); } .video-wrapper video { width: 100%; height: 100%; object-fit: cover; } .video-content { margin: 0; position: absolute; top: 38%; margin-top: 1rem; left: 50%; margin-right: -50%; transform: translate(-50%, -50%); color: #fff; text-align: center; } .video-content h2 { font-size: 2rem; letter-spacing: 0.1rem; text-shadow: 2px 2px 2px #444; } .video-content p { max-width: 600px; padding: 0 2rem; } .video-credit { position: absolute; right: 1rem; bottom: .5rem; color: #ccc; font-size: 12px; } /* チェックボックスの親要素 */ .bgm-opt { position: absolute; left: 1rem; bottom: 1rem; color: #fff; } @media screen and (min-width: 481px) { .video-wrapper { aspect-ratio: 1600 / 897; } .video-content { top: 43%; margin-top: 0; } } @media only screen and (min-width: 768px) { .video-content h2 { font-size: 2.5rem; } .video-content p { max-width: 600px; padding: 0 0; } } @media only screen and (min-width: 992px) { .video-content h2 { font-size: 3rem; } } @media only screen and (min-width: 1139px) { .video-content h2 { font-size: 3.5rem; } }
関連ページ:JavaScript フォームとフォームコントロールの使い方
audio 要素で音声データを別途用意
以下は動画とは別に音声データ(mp3 ファイル)を用意して、チェックボックスの状態で音声データの再生・停止を行う例です。
audio 要素には controls 属性を指定せず、非表示にしています。また、loop 属性を指定して繰り返し再生するようにしています。
<div class="video-wrapper"> <div class="video-wrapper"> <video muted autoplay playsinline loop poster="poster.jpg"> <source src="sample.webm" type="video/webm"> <source src="sample.mp4" type="video/mp4"> </video> </div> <div class="bgm"> <audio src="sample.mp3" loop></audio> <!-- audio 要素 --> <input type="checkbox" id="music-on1"> <label for="music-on1"> 音声 </label> </div> </div>
JavaScript は前述の例とほぼ同じですが、動画の muted 属性を操作する代わりに、audio 要素の play() と pause() で音声の再生・停止を行っています。
document.addEventListener('DOMContentLoaded', () => { const videoWrappers = document.querySelectorAll('.video-wrapper'); videoWrappers.forEach((wrapper) => { // audio 要素を取得 const audio = wrapper.querySelector('audio'); // 必要に応じてボリュームを調整 audio.volume = .7; // チェックボックスの要素 const checkbox = wrapper.querySelector('input[type="checkbox"]'); // チェックボックスの change イベントにリスナーを設定 checkbox.addEventListener('change', (e) => { if (e.currentTarget.checked) { // チェックされれば audio 要素を再生 audio.play(); } else { // チェックが外されれば再生を停止 audio.pause(); } }); }); }, false);
.video-wrapper { aspect-ratio: 1/1; width: 100%; max-width: 1600px; margin: auto; position: relative; } .video-wrapper video { width: 100%; height: 100%; object-fit: cover; object-position: 50% 0%; } .bgm { position: absolute; left: 1rem; bottom: 1rem; color: #fff; } @media screen and (min-width: 480px) { .video-wrapper { aspect-ratio: 16/9; } }
video のメソッド
video 要素では以下のようなメソッドを利用できます。
メソッド | 説明 |
---|---|
load() | ビデオを先頭にリセットし、最適なソースを選択してビデオを読み込むプロセスを開始します。先読みされるビデオデータの量は、要素の preload 属性の値によって決まります。 |
pause() | 再生を一時停止します。 |
play() | 再生を開始します。play() メソッドは再生が開始されたときに解決または拒否される Promise を返します。 |
fastSeek() | 低い精度で素早く指定時刻にシークします(※ Chrome ではサポートされていないのでエラー)。代わりに currentTime プロパティを設定すればその時刻にシークします。 |
以下はホバー時に動画を再生し、マウスアウトすると一時停止する例です。タッチデバイスの場合は、動画をタップすると再生され、動画以外の部分をタップすると一時停止します。
<div class="hover-video-wrapper"> <video src="sample.mp4" poster="poster.jpg"></video> </div>
この例の場合、動画の親要素(.hover-video-wrapper)の mouseenter と mouseleave イベントを使って、動画の再生と一時停止を設定しています。また、video 要素の属性を JavaScript のプロパティを使って設定していますが、video 要素に HTML で設定した方が管理しやすいかも知れません。
document.addEventListener('DOMContentLoaded', () => { const hoverVideoWrappers = document.querySelectorAll('.hover-video-wrapper'); hoverVideoWrappers.forEach((wrapper) => { // video 要素 const video = wrapper.querySelector('video'); // プロパティを設定 video.muted = true; video.setAttribute('playsinline', ''); video.preload = 'auto'; // .hover-video-wrapper の mouseenter イベント wrapper.addEventListener('mouseenter', ()=> { video.play(); // 再生 }, false); // .hover-video-wrapper の mouseleave イベント wrapper.addEventListener('mouseleave', ()=> { video.pause(); // 一時停 }, false); }); });
.hover-video-wrapper { aspect-ratio: 1618/1000; width: 100%; max-width: 600px; position: relative; overflow: hidden; } /* オーバーレイ */ .hover-video-wrapper::before { content: ''; position: absolute; top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(0, 0, 0, 0.4); transition: background-color 1s; /* トランジション */ } /* ホバー時のオーバーレイ */ .hover-video-wrapper:hover::before { background-color: rgba(0, 0, 0, 0); } .hover-video-wrapper video { width: 100%; height: 100%; object-fit: cover; }
以下はボタンをクリックすると動画の再生と一時停止をトグルする例です。また、Back ボタンをクリックすると currentTime プロパティを使って動画を先頭に戻します。
<div class="js-video-wrapper"> <video src="sample.mp4" poster="poster.jpg"></video> <button class="play" type="button">Play</button> <button class="back" type="button">Back</button> </div>
play() メソッドは Promise を返すので、その Promise を監視することで実際の再生状態が判定できます。
await を指定した Promise が rejected となった場合はエラーをスローするので try...catch 構文を使って判定しています。
また、ビデオの再生終了の検出は ended イベントを利用しています。
document.addEventListener('DOMContentLoaded', () => { const videoWrappers = document.querySelectorAll('.js-video-wrapper'); videoWrappers.forEach((wrapper) => { // video 要素 const video = wrapper.querySelector('video'); video.muted = true; video.setAttribute('playsinline', ''); video.preload= 'auto'; // Play ボタン const play = wrapper.querySelector('button.play'); // Back ボタン const back = wrapper.querySelector('button.back'); // Play ボタンに click イベントのリスナーを登録 play.addEventListener('click', handlePlayBtn, false); // Back ボタンに click イベントのリスナーを登録 back.addEventListener('click', handleBackBtn, false); // video 要素に ended イベントのリスナーを登録 video.addEventListener('ended', videoEnded, false); // ビデオを再生する非同期関数 async function playVideo() { try { // await を指定して Promise が確定するまで待つ await video.play(); // Promise が解決されたらボタンに playing クラスを追加してテキストを変更 play.classList.add('playing'); play.textContent = 'Pause'; } catch (err) { // 再生が開始されない場合(Promise が拒否された場合)はクラスを削除 play.classList.remove('playing'); } } // Play ボタンのクリックイベントのリスナー function handlePlayBtn() { // ビデオが一時停止状態の場合(読み取り専用の paused プロパティで判定) if (video.paused) { // playVideo()を呼び出す playVideo(); } else { // 一時停止状態でなければ、一時停止しクラスを削除しテキストを変更 video.pause(); play.textContent = 'Play'; play.classList.remove('playing'); } } // Back ボタンのクリックイベントのリスナー function handleBackBtn() { // ビデオが一時停止状態の場合 if (video.paused) { // 先頭に戻す video.currentTime = 0; } else { // 一時停止状態でなければ、一時停止して先頭に戻し、クラスを削除しテキストを変更 video.pause(); video.currentTime = 0; play.textContent = 'Play'; play.classList.remove('playing'); } } // ビデオが終了した際に発生する ended イベントのリスナー function videoEnded() { // ビデオが終了したら、ボタンのラベルを変更し playing クラスを削除 play.textContent = 'Play'; play.classList.remove('playing'); } }); });
参考サイト:
.js-video-wrapper { margin: 50px 0; } .js-video-wrapper video { aspect-ratio: 1618/1000; width: 100%; height: auto; max-width: 600px; display: block; } .js-video-wrapper button { padding: 8px 20px; color: #fff; width: 7rem; margin: 10px 10px 0 0; border: none; } .js-video-wrapper button.play { background-color: #589462; } .js-video-wrapper button.back { background-color: #5a6faf; } .js-video-wrapper button.play.playing { background-color: #dd7703; }
video のイベント
video 要素では以下のようなイベントを利用できます。
これらのイベントを受け取るには、addEventListener() を使用するか、「onイベント名」プロパティ(onevent ハンドラー)にイベントリスナーを設定します。
イベント | 発生するタイミング |
---|---|
abort | リソースを完全に読み込めなかったとき(エラーが原因でない場合) |
canplay | 再生できる状態になったとき(途中でバッファリングのために停止する可能性あり) |
canplaythrough | 最後まで再生できる状態になったとき(最後まで再生するのに十分なデータが読み込まれた) |
ended | メディアの終わりに達した(またはそれ以上利用できるデータがない)とき |
error | エラー(ネットワーク接続の問題など)によりリソースが読み込めなかったとき |
loadeddata | メディアの1フレーム目の読み込みが終了したとき |
loadedmetadata | リソースのメタデータ(再生時間などのメタ情報)が読み込まれたとき |
loadstart | リソースの読み込みを開始したとき |
pause | 再生を一時停止したとき。pause() メソッドが呼び出されたとき |
play | 再生を開始したとき。play() メソッド、または autoplay 属性の結果として、paused プロパティが true から false に変更されたとき |
progress | ブラウザーがリソースを読み込む際に発生 |
ratechange | 再生速度(再生レート)が変更されたとき |
seeked | シーク動作が完了したとき(現在の再生位置が変更され、論理属性の seeking が false に変更されたとき)。 |
seeking | シーク動作が開始されたとき |
stalled | リソースのデータを読み込もうとしたがデータが得られなかったとき |
suspend | リソースの読み込みが中断されたとき |
timeupdate | 再生時刻を表す currentTime プロパティの値が更新されたとき |
volumechange | 音量を変更したとき。または mute 属性を変更したとき。 |
waiting | 一時的なリソースのデータ不足で再生が停止したとき(データの読み込みが遅く、再生を続けられないとき) |
以下は video 要素の canplaythrough イベントに addEventListener() と oncanplaythrough ハンドラでリスナーを登録する例です。
いずれも「canplaythrough:動画全体を再生できるはずです」とコンソールに出力されます。
// video 要素を取得 const video = document.querySelector('video'); // addEventListener で canplaythrough イベントにリスナーを設定 video.addEventListener('canplaythrough', (event) => { console.log(event.type + ':動画全体を再生できるはずです'); }); // oncanplaythrough で canplaythrough イベントにリスナーを設定 video.oncanplaythrough= (event) => { console.log(event.type + ':動画全体を再生できるはずです'); };
以下は autoplay 属性を指定したビデオで発生するイベントを出力する例です。
<video id="eventTarget" src="sample.mp4" controls muted autoplay playsinline poster="poster.jpg"></video> <button type="button" id="clear-events">Clear Events</button> <ol id="events"></ol>
ファイルの読み込みやコントロールの操作(再生ボタンや停止ボタン、シークバーのクリック、再生速度の変更)、ビデオ終了時に発生するイベントが出力されます。
但し、タイミングにより検出されるイベントが異なります(loadstart が検出されなかったり、play のみが検出されるなど)。そのため、再読込すると出力される内容が異なる場合があります。
また、Chrome や Firefox ではシークバーを操作すると canplay や canplaythrough が表示(検出)されますが、Safari では seeked のみが検出されます。
この例の場合、DOMContentLoaded を使うと、loadstart イベントを検知できなかったので以下は DOMContentLoaded を使用していません。
const video = document.getElementById('eventTarget'); const ol = document.getElementById('events'); // 各イベントにリスナー(listEvents)を登録 video.addEventListener('loadstart', listEvents); video.addEventListener('loadeddata', listEvents); video.addEventListener('loadedmetadata', listEvents); video.addEventListener('canplay', listEvents); video.addEventListener('canplaythrough', listEvents); video.addEventListener('play', listEvents); video.addEventListener('pause', listEvents); video.addEventListener('ratechange', listEvents); video.addEventListener('seeked', listEvents); video.addEventListener('ended', listEvents); // リスナーの定義 function listEvents(e) { // li 要素を生成 const li = document.createElement('li'); // 生成した li 要素のテキストにイベント名(e.type)を設定 li.textContent = e.type; ol.appendChild(li); } document.getElementById('clear-events').addEventListener('click', ()=> { ol.innerHTML = ''; });
以下は、video 要素の canplaythrough イベントを検知したら、ポジションのアニメーションを実行し、アニメーションが終了したら動画を再生する例です。
<div class="js-video-wrapper"> <video src="sample.mp4" controls poster="poster.jpg"></video> </div>
アニメーションは Web Animation API を使って、終了したら onfinish イベントハンドラを使って動画の再生を開始しています。
document.addEventListener('DOMContentLoaded', () => { // .video-wrappe を取得 const jsVideoWrappers = document.querySelectorAll('.js-video-wrapper'); // 取得した各 .video-wrappe で jsVideoWrappers.forEach((wrapper) => { // video 要素を取得 const video = wrapper.querySelector('video'); video.muted = true; video.setAttribute('playsinline', ''); video.preload = 'auto'; // video 要素に canplaythrough イベントのリスナを設定 video.addEventListener('canplaythrough', () => { // ポジションを移動するアニメーション const slideUp = video.animate( { objectPosition: ['50% 100%', '50% 0%'] }, { duration: 1500, easing: 'ease-out', } ); // アニメーションが終了したら動画を再生 slideUp.onfinish = () => { video.play(); } }, false); }); }, false);
.js-video-wrapper { aspect-ratio: 2/1; width: 100%; } .js-video-wrapper video { width: 100%; height: 100%; object-fit: cover; object-position: 50% 0%; }
Chrome や Firefox ではシークバーをクリックすと canplay や canplaythrough イベントが発生するので、再度ポジションのアニメーションが実行され、その後動画が再生されます(Safari ではなりません)。
繰り返し再生される
Chrome や Firefox では currentTime プロパティを操作すると、canplay や canplaythrough イベントが発生するため、以下を記述すると0〜5秒の間を繰り返し再生されてしまいます。
31行目で currentTime に0を設定していますが、canplaythrough イベントが発生して繰り返し再生されます。ループさせないようにするには、addEventListener() の第3引数に {once: true} を指定します。
但し、Safari では currentTime プロパティを操作しても canplay や canplaythrough イベントは発生しないので、繰り返し再生されません。
document.addEventListener('DOMContentLoaded', () => { const jsVideoWrappers = document.querySelectorAll('.js-video-wrapper'); jsVideoWrappers.forEach((wrapper) => { const video = wrapper.querySelector('video'); video.muted = true; video.setAttribute('playsinline', ''); video.preload = 'auto'; video.addEventListener('canplaythrough', () => { const slideUp = video.animate( { objectPosition: ['50% 100%', '50% 0%'] }, { duration: 1500, easing: 'ease-out', } ); slideUp.onfinish = () => { video.play(); } }, false); //ループさせない場合は第3引数に {once: true} を指定 // timeupdate を監視 video.addEventListener('timeupdate', () => { // currentTime が5以上になったら if (video.currentTime >= 5) { // 一時停止 video.pause(); // currentTime に0を設定(ループする) video.currentTime = 0; } }, false); }); }, false);
指定した範囲を繰り返し再生
以下は指定した範囲を繰り返し再生する例です。
この例では、js-loop クラスを指定した video 要素に data-start-time と data-end-time(カスタム属性)で指定された範囲で繰り返し再生します。
<video class="js-loop" data-start-time="5" data-end-time="10" src="sample.mp4" controls playsinline poster="poster.jpg"></video>
JavaScript ではカスタム属性に指定された値を dataset プロパティで取得して、それらが設定されていれば timeupdate を監視します。
currentTime が data-end-time の値より大きくなれば、currentTime を data-start-time の値に設定して再生位置を戻しています。
timeupdate イベントの頻度はシステムの稼働状況に依存するので、指定した正確な範囲を繰り返すことはできません。
また、iOS(iPhone)ではビデオをロードする前に currentTime に0以外の値を設定すると正しく動作しないので、最初に currentTime に値を設定する際は loadeddata イベントで設定しています(または、preload に metadata を設定します。iPhone で currentTime が設定できない)。
document.addEventListener('DOMContentLoaded', () => { const jsLoopVideos = document.querySelectorAll('.js-loop'); jsLoopVideos.forEach((video) => { video.muted = true; //video.autoplay = true; // 自動再生する場合 // カスタム属性の値を dataset プロパティで取得して数値に変換 const startTime = parseInt(video.dataset.startTime); const endTime = parseInt(video.dataset.endTime); //カスタム属性の値が設定されていれば if(startTime && endTime) { // 開始時刻を設定(iOS でバグ?のため loadeddata で currentTime を設定) video.addEventListener('loadeddata', ()=> { video.currentTime = startTime; }); // timeupdate を監視 video.addEventListener('timeupdate',loop, false); } //リスナー関数 function loop() { if( video.currentTime >= endTime ) { //現在の再生時刻が指定された終了時刻を超えたら開始時刻を設定 video.currentTime = startTime; } } }); }, false);
.js-loop { aspect-ratio: 16/9; width: 70%; height: auto; max-width: 500px; }
以下を再生すると、開始5秒の時点からおよそ10秒の間を繰り返します。
Media Fragments URI
再生する範囲を指定する方法として Media Fragments URI という特別な URI の指定方法があります。但し、一部のブラウザは対応してません。
<audio> または <video> 要素に対してメディアの URI を src 属性に指定する際に、再生するメディアの再生範囲を指定するための追加情報を記述することができます。
以下の書式でハッシュマーク (#) に続いてメディアフラグメントの記述を追加します。
#t=[starttime][,endtime]
例えば、以下のように #t=
に時間を指定すると動画の3秒~10秒の部分を再生します。
<video src="sample.mp4#t=3,10"></video>
iPhone で currentTime が設定できない
currentTime プロパティに値を設定すると指定された時刻にシーク(移動)するはずですが、iPhone では初期状態(ロード前)で値に 0 以外を設定すると正しく動作しません。
0を指定する場合は問題ありません(2023年4月の時点)。
<video src="sample.mp4" controls playsinline poster="poster.jpg"></video>
以下は currentTime プロパティに値に 0 以外を設定する例です。
const video = document.querySelector('video'); video.currentTime = 13; // currentTime に 0 以外を設定
iPhone 以外では currentTime プロパティに指定した時刻から動画を開始(再生)することができます。
iPhone の場合は、 currentTime プロパティの設定は無視され、最初(0の位置)から再生されてしまい、2回目以降は再生できません(再生するとシークバーの時刻のみは13になっている)。
また、以下を iPhone で見ると、poster 属性に代替画像を設定しているにも関わらす、再生ボタンのみが表示され正常に動作しません。
解決策
ビデオが最初にロードされたときに、フレームがまだロードされていないと、iPhone の場合、currentTime の(0 以外への)変更を許可しないようです。
解決策の1つは前述の例同様、loadeddata イベントを使って currentTime を設定する方法です。
const video = document.querySelector('video'); //loadeddata イベントで currentTime を設定 video.addEventListener('loadeddata', ()=> { video.currentTime = 13; });
上記の場合、iPhone でも currentTime で指定した13秒の位置から再生でき、また、 poster 属性に指定したポスター画像も表示されます。
preload="metadata" を追加
もっと、簡単な方法は video 要素の preload 属性に "metadata" を設定するだけです。
<video src="sample.mp4" preload="metadata" controls playsinline poster="poster.jpg"></video><!-- preload="metadata" を追加 -->
HTML の属性ではなく、JavaScript で設定しても同じです。
const video = document.querySelector('video'); // preload プロパティに "metadata" を設定 video.preload = 'metadata'; video.currentTime = 13;
preload="metadata" を追加した場合、以下のいずれで currentTime を設定しても正常に動作しました。
// preload="metadata" を追加していれば、単に以下でOK video.currentTime = 13; //loadedmetadata イベントで currentTime を設定(preload="metadata" の追加が必要) video.addEventListener('loadedmetadata', ()=> { video.currentTime = 13; }); //loadeddata イベントで currentTime を設定(preload="metadata" の追加は不要) video.addEventListener('loadeddata', ()=> { video.currentTime = 13; });
「HTML5 Video - currentTime not setting properly on iPhone」を参考にさせていただきました。
他のビデオの再生を停止
以下はそのページで1つの動画だけを再生する例です。動画の再生ボタンをクリックすると、その動画は再生されますが、他に再生中の動画があれば全て一時停止されます。
addEventListener でドキュメントの play イベントにリスナーを設定し、第3引数(useCapture)を true に設定します。リスナーでは全ての video 要素を取得して、play イベントが発生した要素が自身でなければ pause() メソッドで一時停止させます。
document.addEventListener('DOMContentLoaded', () => { // ドキュメントの play イベント document.addEventListener('play', (e) => { // 全ての video 要素を取得 const videos = document.querySelectorAll('video'); videos.forEach((video) => { // play イベントが発生した要素が自身でなければ一時停止 if(video !== e.target) { video.pause(); } }); }, true); // addEventListener()の第3引数(useCapture)を true に }, false);
以下はそのページの exv クラスが指定された video 要素を対象にする場合の例です(このページでは一部の動画に対して以下を設定しています)。
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('play', (e) => { // 全ての exv クラスが指定された video 要素を取得 const exclusiveVideos = document.querySelectorAll('.exv'); exclusiveVideos.forEach((exv) => { if(exv !== e.target) { exv.pause(); } }); }, true); // useCapture を true に }, false);
動画の軽量化
動画のファイルサイズが大きいと読み込みに時間がかかるため軽量化してファイルサイズを小さくする必要があります。
動画ファイルを軽量化するソフト(ツール)は色々ありますが、HandBrake という無料のソフトを使えば、比較的簡単にファイルを軽量化したり、動画形式を mp4 や webm に変換することができます。
HandBrake を使った動画の軽量化の方法は以下のページを御覧ください。