• #tokyo-soundscape
  • #nuxt
  • #vue
  • #calendar
  • #security
  • #composable
tokyo-onkeiメモ

収録カレンダーとスポット管理の実装

2026年2月14日。Tokyo Soundscapeプロジェクトのランディングページに、年間収録カレンダーとスポット管理の仕組みを入れた。メインの作業は3つ。GPS座標を公開ページから排除するセキュリティ対策、年間カレンダーのUI実装、そして桜シーズンに向けたスポット追加。

GPS座標・録音スケジュールの非公開化

収録スポットにはGPS座標や具体的な録音スケジュールを持たせているが、これらをそのまま公開ページに載せるのはリスクがある。行動パターンが読まれるし、機材を持っている場所と時間が特定される。

対策として、公開向けのデータエクスポート(publicSpots)と内部データを分離した。公開側には座標やスケジュールの詳細を含めず、tree-shakingによって内部データがバンドルに混入しない設計にした。ビルド後の出力を確認して、内部データがクライアントバンドルに含まれていないことも検証済み。

// 公開用には座標・スケジュールを含めない
export const publicSpots = allSpots.map(({ coordinates, schedule, ...rest }) => rest)

年間収録カレンダーの実装

月別グリッドから年間一覧表へ

最初は月ごとのカレンダーグリッド(普通のカレンダーUI)で実装した。が、全体を俯瞰したいという要件で年間一覧表に変更。縦軸が日付(1~31日)、横軸が月(1~12月)のテーブル形式で、1年分の収録予定が一覧できる。

useSpotCalendar composableを作って、スポットデータからカレンダーのセルデータを生成するロジックを切り出した。

イベント列の追加

各月の列の横にイベント列を追加した。その日に収録予定があるスポットを、3~4文字の略称(「浅草」「千鳥」など)で色付きバッジとして表示する。スポットごとに固有の色を割り当てているので、カレンダーをぱっと見たときに収録の分布が視覚的にわかる。

スケジュールルールのサイドバー

カレンダーの横に、スケジュールルールを2列で並べるサイドバーを設けた。

  • 定期イベント: 毎月第1土曜の朝録音、など
  • 年間イベント: 花火大会、祭り、桜シーズンなど季節もの

テーブルの横幅は最終的に100%に広げて、画面幅いっぱいに使うようにした。

桜スポットの追加

春に向けて、桜の収録スポットを4箇所追加した。

  • 浅草寺
  • 千鳥ヶ淵
  • 目黒川
  • 上野公園

桜の見頃は年によってズレるが、スポットごとに日程を数日ずつずらして設定した。満開のタイミングが全箇所同じということはまずないので、期間をばらすことで録り逃しを減らす狙い。

年間サマリーの自動計算

カレンダーデータから年間の収録サマリーを自動で算出する仕組みも入れた。

  • 163セッション(年間の収録回数)
  • 55稼働日(実際に現場に出る日数)
  • 163時間(生録音の合計時間)
  • 約49時間(編集後の推定時間)

これらの数値はスポットデータとスケジュールルールから計算されるので、スポットを追加・削除すれば自動的に更新される。

UIの調整

SpotCardのホバーアニメーション削除

スポット一覧のカードにホバーアニメーション(拡大+影)を付けていたが、やりすぎ感があったので削除した。シンプルにカーソルが乗ったときの背景色変更だけに。

写真の明るさ調整

スポットカードの写真が暗すぎたので、CSSフィルタで明るさを調整した。

Unsplashからの写真取得

山手線の駅周辺の写真をUnsplash APIで取得して、スポットカードのサムネイルに使用した。

ふりかえり

年間カレンダーは月別グリッドよりテーブル形式のほうが俯瞰性が高い。12ヶ月 x 31日のグリッドは情報密度が高いが、イベント列の色付きバッジがあると意外と読みやすくなる。

GPS座標の非公開化は早めにやっておいてよかった。公開/非公開の境界をデータ構造のレベルで分離し、tree-shakingに頼れる設計にしたことで、うっかり漏れるリスクを構造的に排除できた。