動画/アニメのループ・自動再生とアクセシビリティ実務
要点(TL;DR)
- 自動再生はmuted+playsInlineが前提、ユーザー操作を常に優先。
理由: ブラウザの自動再生ポリシーと低電力/省データ時のブロック対策。 - prefers-reduced-motion一致時は停止/静止画代替/1回のみ再生に切替。
失敗時リスク: 体調不良やUX悪化で離脱・苦情につながる。 - LCP/CLSを守るためposter+予約サイズを必ず指定、遅延ロードを基本に。
根拠: レイアウトシフト回避と初期描画の安定化。
1. 埋め込みテンプレ(安全なデフォルト)
<video
src="/anim/ui.mp4"
autoplay muted loop playsinline
width="420" height="240"
aria-label="操作の流れ(再生は自動で停止可能)"
></video>
poster は初期表示のLCPを悪化しないよう適切に圧縮した静止画を指定します。
1.5 ブラウザの自動再生ポリシー(要点)
- muted でない自動再生は原則ブロック。ユーザー操作(タップ/クリック)を契機に。
- playsInline が無いと iOS Safari はフルスクリーン化 → UI崩れ/意図せぬ音量ボタン誘発。
- 省データ/低電力モード時は自動再生が無効化されるケースあり → 代替表示を必ず用意。
データセーバー配慮
判定は一律ではありません。判定不能時もあるため、自動再生を前提にしないUI(静止画+再生ボタン)を優先。
1.8 プラットフォーム別ポリシー速見表
代表的な環境の傾向。個々のバージョン差は公式ドキュメントを確認。
| 環境 | 自動再生(音声) | muted+inline | 省データ/低電力 | 備考 |
|------|-----------------|--------------|------------------|------|
| iOS Safari | × | ○(必須) | ×/× によって停止 | フルスクリーン化を避ける |
| Android Chrome | △ | ○ | △ | Data Saver で停止あり |
| Desktop Chrome | △ | ○ | – | ユーザー操作履歴に依存 |
| Desktop Safari | △ | ○ | – | 自動再生許可設定あり |
2. reduced-motion(動きが苦手な利用者への配慮)
@media (prefers-reduced-motion: reduce){
video[autoplay]{
animation: none !important;
}
}
JS で matchMedia('(prefers-reduced-motion: reduce)')
を検出し、再生停止/静止画に切り替えるのも有効です。
// Reactの例: reduced-motion なら自動停止 + ポスターに置換
import { useEffect, useRef } from 'react';
export function SafeAutoPlayVideo(){
const ref = useRef<HTMLVideoElement>(null);
useEffect(() => {
const mq = window.matchMedia('(prefers-reduced-motion: reduce)');
const v = ref.current; if(!v) return;
if (mq.matches) { v.pause(); v.removeAttribute('autoplay'); }
}, []);
return <video ref={ref} autoPlay muted loop playsInline width={420} height={240} poster="/poster/ui.jpg" />
}
2.5 ユーザー主導の制御(アクセシビリティ)
キーボード操作やスクリーンリーダーでの可用性を担保します。
<button aria-pressed="false" aria-controls="demo" id="toggle">再生</button>
<video id="demo" muted playsinline width="420" height="240" aria-label="UIの操作例"></video>
<script>
const btn = document.getElementById('toggle');
const v = document.getElementById('demo');
btn.addEventListener('click', async () => {
const playing = btn.getAttribute('aria-pressed') === 'true';
if (playing) { v.pause(); btn.setAttribute('aria-pressed','false'); btn.textContent='再生'; }
else { await v.play().catch(()=>{}); btn.setAttribute('aria-pressed','true'); btn.textContent='停止'; }
});
</script>
3. 代替テキスト/字幕
<video ... aria-label="ボタンの状態遷移">
<track kind="captions" src="/captions/ui.vtt" srclang="ja" label="日本語" default />
</video>
意味伝達が重要なら字幕/説明文を併用し、動きに依存しない理解手段を提供します。
3.5 パフォーマンス設計(LCP/INPを壊さない)
- poster を軽量化し LCP を守る(WebP/AVIF, 先読み禁止)。
- 初期は
preload="none"
ormetadata
。視認直前に 遅延ロード。 - モバイルでは 短尺・低fps を優先(8–15fps でも UIは伝わることが多い)。
3.8 視認外は自動停止(IntersectionObserver)
const io = new IntersectionObserver((entries)=>{
for (const e of entries){
const v = e.target as HTMLVideoElement;
if (e.isIntersecting) v.play().catch(()=>{}); else v.pause();
}
}, { threshold: 0.25 });
document.querySelectorAll('video[autoplay][muted]').forEach((v)=> io.observe(v));
スクロールで見えないときは停止し、CPU/バッテリーを節約します。
4. 公開前チェック
- 自動再生は muted/playsinline で安全
- 予約サイズ指定で CLS が発生していない
- reduced-motion で停止/静止画へ切り替わる
- ARIA/字幕で意味が補完されている
- 省データ/低電力モード時の挙動が破綻しない
- 音声付きケースは初回ミュート + 明確な再生ボタン
- IntersectionObserver 等で視認外は自動停止できる
5. 実機検証プロトコル(10分)
- iOS/Android/デスクトップの3系で 初回訪問 をテスト(シークレットウィンドウ)。
- 省データ/低電力モードをONにし、代替UIが出るか確認。
- キーボード操作で再生/停止/フォーカス遷移が可能か。
- LCP/CLS/INP を簡易計測(DevTools/Performance, Web Vitals 拡張)。
6. 実装パターン集
- 静止画+再生ボタン(最も無難)
- スクロールインで1回だけ再生 → 以後は停止
- reduce-motion 時は常に静止画表示
- タップで音声ON+再生、初回はミュート
FAQ
FAQ(拡張)
1自動再生に preload
は必要?
初期は
none/metadata
を推奨。視認直前に遅延ロード。2アニメGIFの代わりに動画?
ほぼ常に動画が有利(容量/画質)。ただし自動再生配慮は必須。
31回だけ再生して止めたい。
loop
を外し、ended
イベントでポスターへ戻す。