開発misc-dev完了
animation-demo-2: スクロールアニメーション教材コンポーネント
概要
/animation-demo-2 は、簿記教材(手形の振り出し)向けのインタラクティブな学習ページ。スクロールに連動して画像が切り替わり、各ステップに対応する音声解説が自動再生される。
目的
- 視覚的な学習体験: スクロールに応じて関連する図解が表示される
- 音声による補足説明: 各ステップの内容を音声で解説
- 直感的なナビゲーション: キーボードショートカットやボタンでステップ移動
ファイル構成
apps/web/
├── app/pages/
│ └── animation-demo-2.vue # メインコンポーネント
└── public/audio/
└── boki3/chapter4_0/ # 音声ファイル格納場所
├── boki3_ch4_0_00.wav
├── boki3_ch4_0_01.wav
├── boki3_ch4_0_02.wav
├── boki3_ch4_0_03.wav
├── boki3_ch4_0_04.mp3
└── boki3_ch4_0_05.mp3
データ構造
各ステップは以下の構造を持つオブジェクトの配列で定義する。
interface Step {
id: string; // ユニークID(例: 'boki3_ch4_0_00')
title: string; // ステップのタイトル
description: string; // 説明文(**text** でボールド)
imageUrl: string; // 図解画像のURL
audioUrl: string; // 音声ファイルのパス
}
ID命名規則
{コース}_{チャプター}_{ステップ番号}
例: boki3_ch4_0_00
└─簿記3級
└─チャプター4.0
└─ステップ00
実装された機能
1. スクロール連動アニメーション
- IntersectionObserverでステップの可視状態を監視
- スクロール位置に応じて画像がクロスフェードで切り替わる
- 左側にテキスト、右側に画像(sticky配置)
2. 音声再生機能
自動再生
- ページ読み込み時、最初のステップの音声を自動再生
- ステップ切り替え時に該当ステップの音声を自動再生
- ブラウザの自動再生ポリシーでブロックされた場合は無視
音声コントロールUI
- 自動再生トグル: チェックボックス + 「自動再生」テキスト
- オン: 青いチェックマーク、ステップ切り替えで自動再生
- オフ: 赤い取り消し線、手動再生のみ
- 再生/一時停止ボタン: 手動で音声を制御
- プログレスバー: 再生位置の表示、クリックでシーク
- 時間表示: 現在位置 / 総時間(mm:ss形式)
ページ遷移時の停止
onBeforeRouteLeaveで音声を停止onBeforeUnmountでクリーンアップ- 他のページに移動しても音声が継続再生されない
3. ナビゲーション
- ボタン: 「← 前へ」「次へ →」
- キーボードショートカット:
Ctrl + [: 前のステップCtrl + ]: 次のステップ
- ステップインジケーター: 「1 / 6」形式で現在位置表示
4. レスポンシブ対応
- デスクトップ: 2カラムレイアウト(テキスト左、画像右)
- モバイル(900px以下): 1カラム、画像が上部に固定
今後の拡張予定
sessionStorageによる位置保存
目的: ページリロード時に現在のステップ位置を復元
なぜsessionStorageか:
- タブを閉じたらリセット(学習終了 → 次回は最初から)
- 複数タブで別々の進捗を保持可能
- 誤ってF5を押した場合のみ位置を復元
実装方針:
// ステップ変更時に保存
sessionStorage.setItem('animation-demo-2-step', activeIndex.value);
// ページロード時に復元
const savedStep = sessionStorage.getItem('animation-demo-2-step');
if (savedStep) {
activeIndex.value = parseInt(savedStep);
}
その他の拡張候補
- 学習進捗の永続化(localStorage)
- 複数チャプター対応
- 音声再生速度の調整
- 字幕/トランスクリプト表示
- プリロード機能(次の音声を事前に読み込み)
技術的なポイント
SSR対応
Audio オブジェクトはブラウザAPIのため、サーバーサイドで実行するとエラーになる。
// クライアントサイドのみで実行
if (import.meta.client) {
watch(currentAudioUrl, (newUrl, oldUrl) => {
if (newUrl !== oldUrl) {
loadAudio(newUrl);
}
});
}
Audio要素の管理
各ステップで新しいAudio要素を作成し、古いものは破棄する。
function loadAudio(url) {
// 既存のAudio要素を停止・破棄
if (audioEl.value) {
audioEl.value.pause();
audioEl.value.src = '';
}
// 新しいAudio要素を作成
const audio = new Audio();
audio.src = url;
audio.load();
audioEl.value = audio;
}
これにより canplay イベントが確実に発火し、自動再生が動作する。
関連ドキュメント
- Vue 3 Composition API
- IntersectionObserver API
- Web Audio API / HTMLAudioElement