• #vue
  • #animation
  • #scrollytelling
  • #audio
  • #documentation
開発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