<EventStatus />

タイムテーブル・ステージ進行

ステージ企画や各会場プログラムのタイムテーブルおよび進行状況の可視化。現在時刻に連動した進行中プログラムの自動ハイライトと進捗バー表示に対応。

使い方

参加者向け

import EventStatus from "@/features/event/components/EventStatus";

<EventStatus />

Hooks src/features/event/hooks/useEventData.ts

静的 JSON ファイル public/data/events.json からスケジュールデータを非同期ロードし、現在時刻やフィルターに応じたイベント一覧を提供します。

プロパティ / 関数役割
filteredEventsEvent[]開催日程およびフィルター (1時間以内 / すべて) に適合したイベント配列
filterMode"hour" | "all"現在のフィルターモード (直近1時間以内 / 全表示)
setFilterMode(mode)(mode : "hour" | "all") => voidフィルターモードの切り替え
currentTimeDayjsTimeContext から取得したアプリ共通の現在時刻
getEventTime(timeStr)(timeStr : string) => Dayjs当日の日付と時刻文字列を結合した Dayjs インスタンスを生成
newIndexMapMap<string, number>「すべて」展開時に新規表示されるカードのアニメーション遅延 (Stagger) 計算用インデックスマップ

コード

プログレスバーの進捗率計算

現在時刻 currentTime と企画の開始・終了時刻からミリ秒単位で進捗パーセンテージ (0〜100%) を動的算出します。

src/features/event/utils/calculateProgress.ts
const calculateProgress = (startStr: string, endStr: string, current: Dayjs) => {
  const start = dayjs(`${current.format("YYYY-MM-DD")} ${startStr}`);
  const end = dayjs(`${current.format("YYYY-MM-DD")} ${endStr}`);
  
  if (current.isBefore(start)) return 0;
  if (current.isAfter(end)) return 100;
  
  const totalDuration = end.diff(start);
  const elapsed = current.diff(start);
  return Math.min(100, Math.max(0, (elapsed / totalDuration) * 100));
};

newIndexMap によるアニメーション遅延制御

フィルターを「1時間以内」から「すべて」に切り替えた際、すでに画面上にあった項目は維持し、新しく追加展開されるカードにのみインデックスを割り振って時間差 (Stagger) フェードインさせます。

src/features/event/components/EventStatus.tsx
// useEventData 内で新規要素のみをカウント
const newIndex = newIndexMap.get(event.name);
const isNewItem = newIndex !== undefined;
const enterDelay = isNewItem ? 0.1 + newIndex * 0.06 : 0;

// Framer Motion で新規要素のみ遅延アニメーション
<motion.div
  layout
  initial={isNewItem ? { opacity: 0, y: 30, scale: 0.8 } : false}
  animate={{ opacity: isFinished ? 0.4 : 1, y: 0, scale: 1 }}
  transition={{ delay: enterDelay }}
/>

データ構造 public/data/events.json

開催日程別 (day1, day2, ...) に企画名と開始・終了時刻を定義します。

public/data/events.json
{
  "day1": [
    { "name": "オープニング", "start": "10:00", "end": "10:20" },
    { "name": "ライブステージ", "start": "10:40", "end": "12:00" }
  ],
  "day2": [
    { "name": "ダンスパフォーマンス", "start": "11:00", "end": "12:30" }
  ]
}