aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorYasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com>2026-11-05 13:41:59 +0900
committerYasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com>2026-11-05 13:41:59 +0900
commit92d802a68ed3f2b76fab55c72fcdc94294711421 (patch)
treeee20fed42abef8e1d3d03bbf442ff0b66136a245 /src
parentff9bafe77a5e0dd84b613bb2aae0bf2a9cb84359 (diff)
events: 地図のLeafletをスクロールまで遅延読み込み
初期表示で約145KBのLeafletを読まないよう、地図が画面に近づいた 時点で動的importする。地図の描画・マーカー・現在地ボタンは従来どおり。
Diffstat (limited to 'src')
-rw-r--r--src/components/ScheduleMap.astro32
1 files changed, 24 insertions, 8 deletions
diff --git a/src/components/ScheduleMap.astro b/src/components/ScheduleMap.astro
index bf84dd7..82e37bf 100644
--- a/src/components/ScheduleMap.astro
+++ b/src/components/ScheduleMap.astro
@@ -1,6 +1,7 @@
---
// カレンダーで選んだ日に開催される予定の会場を地図に表示する。
// 会場名から緯度・経度を引くため、公共施設のデータと、イベント会場の追加データを使う。
+// 地図の描画ライブラリ(Leaflet)は重いので、地図が画面に近づくまで読み込まない。
---
<div class="schedule-map-block">
@@ -10,8 +11,8 @@
</div>
<script>
- import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
+ import type { Circle, LatLngTuple, LayerGroup, Marker } from 'leaflet';
import facilities from '@/data/kodaira-facilities.json';
import extraVenues from '@/data/kodaira-event-venues.json';
import boundary from '@/data/kodaira-boundary.json';
@@ -93,8 +94,9 @@
const el = document.getElementById('schedule-map');
const noteEl = document.getElementById('schedule-map-note');
- if (el && noteEl && !el.dataset.initialized) {
- el.dataset.initialized = '1';
+ async function initMap(el: HTMLElement, noteEl: HTMLElement) {
+ // Leaflet はこの関数が呼ばれたときに初めて読み込む(別チャンクになる)。
+ const L = (await import('leaflet')).default;
const map = L.map(el, {
scrollWheelZoom: false,
@@ -143,9 +145,9 @@
else map.fitBounds(currentBounds, { padding: [40, 40], maxZoom: 16 });
};
- let currentBounds: L.LatLngTuple[] = [];
+ let currentBounds: LatLngTuple[] = [];
- let markers: L.LayerGroup | null = null;
+ let markers: LayerGroup | null = null;
// 現在地表示。位置情報はボタンを押したときだけ取得する。
const locateStatusEl = document.getElementById('schedule-map-locate-status');
@@ -160,8 +162,8 @@
const ACTIVE_ICON =
'<span aria-hidden="true"><svg viewBox="0 0 24 24" width="20" height="20"><circle cx="12" cy="12" r="2.5" fill="currentColor"/><circle cx="12" cy="12" r="7" fill="none" stroke="currentColor" stroke-width="2"/></svg></span>';
- let locationMarker: L.Marker | null = null;
- let accuracyCircle: L.Circle | null = null;
+ let locationMarker: Marker | null = null;
+ let accuracyCircle: Circle | null = null;
let locateButton: HTMLButtonElement | null = null;
const updateLocateButton = () => {
@@ -329,7 +331,7 @@
return;
}
- const bounds: L.LatLngTuple[] = [];
+ const bounds: LatLngTuple[] = [];
for (const { venue, items } of groups.values()) {
bounds.push([venue.lat, venue.lon]);
@@ -383,4 +385,18 @@
window.setTimeout(() => refreshMap(true), 300);
window.addEventListener('resize', () => refreshMap(false));
}
+
+ // 地図が画面に近づいてから Leaflet を読み込む(初期表示を軽くする)。
+ if (el && noteEl && !el.dataset.initialized) {
+ const observer = new IntersectionObserver(
+ (entries, obs) => {
+ if (!entries.some((entry) => entry.isIntersecting)) return;
+ obs.disconnect();
+ el.dataset.initialized = '1';
+ void initMap(el, noteEl);
+ },
+ { rootMargin: '300px 0px' }
+ );
+ observer.observe(el);
+ }
</script>