diff options
| author | Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> | 2026-11-06 19:22:18 +0900 |
|---|---|---|
| committer | Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> | 2026-11-06 19:22:18 +0900 |
| commit | 3df9fffd0e927f92dffe8b986d1ab8c61d4b6484 (patch) | |
| tree | 532b2ab8489da60117749b37b56a77a4e387492a | |
| parent | 0c0c5de8c6bc2e06d0749188a9717475e1df60aa (diff) | |
parks: 公園を緑色で点滅させ、地図のリンクを2つに分ける
通常時は公園を緑色で点滅、絞り込み時は明るい緑で強調。近隣公園も同じ緑のスタイルにした。選択バーに「Googleマップ」と「徒歩の経路」を並べた。施設アイコンのズームを遊具と同じ17に揃え、消火栓・防火水槽はチェック時のみ表示する。
| -rw-r--r-- | src/components/ParkMap.astro | 128 |
1 files changed, 89 insertions, 39 deletions
diff --git a/src/components/ParkMap.astro b/src/components/ParkMap.astro index a185fe3..6c10d93 100644 --- a/src/components/ParkMap.astro +++ b/src/components/ParkMap.astro @@ -101,9 +101,12 @@ background: #f1f5f9; } :global(.leaflet-overlay-pane path.park-selected) { - filter: drop-shadow(0 0 6px rgba(230, 81, 0, 0.95)); + filter: drop-shadow(0 0 6px rgba(27, 94, 32, 0.95)); animation: park-selected-pulse 1.2s ease-in-out infinite; } + :global(.leaflet-overlay-pane path.park-blink) { + animation: park-blink-pulse 2.6s ease-in-out infinite; + } @keyframes park-selected-pulse { 0%, 100% { @@ -115,11 +118,26 @@ stroke-opacity: 0.35; } } + @keyframes park-blink-pulse { + 0%, + 100% { + fill-opacity: 0.45; + } + 50% { + fill-opacity: 0.12; + } + } @media (prefers-reduced-motion: reduce) { - :global(.leaflet-overlay-pane path.park-selected) { + :global(.leaflet-overlay-pane path.park-selected), + :global(.leaflet-overlay-pane path.park-blink) { animation: none; } } + /* 消火栓・防火水槽はチェックオンのときだけ表示する */ + :global(body:not(.show-fire) .park-fac-tank), + :global(body:not(.show-fire) .park-fac-hydrant) { + display: none; + } .park-map-legend { display: flex; @@ -483,63 +501,70 @@ import parks from "@/data/kodaira-parks.json"; const MATCHED_STYLE = { - color: "#b71c1c", + color: "#2e7d32", weight: 2, - fillColor: "#e53935", - fillOpacity: 0.45, + fillColor: "#66bb6a", + fillOpacity: 0.35, }; const UNMATCHED_STYLE = { - color: "#ef5350", + color: "#a5d6a7", weight: 1.2, - fillColor: "#ffcdd2", - fillOpacity: 0.3, + fillColor: "#c8e6c9", + fillOpacity: 0.25, }; const DIM_STYLE = { - color: "#ef9a9a", + color: "#c8e6c9", weight: 0.8, - fillColor: "#ffebee", - fillOpacity: 0.03, + fillColor: "#e8f5e9", + fillOpacity: 0.05, opacity: 0.2, }; const SELECTED_STYLE = { - color: "#e65100", - weight: 5, - fillColor: "#ffd54f", - fillOpacity: 0.8, - }; - - // 選択中の公園を強調するためのクラス(グロー表示) - const markSelected = (lyr: any, selected: boolean) => { - const element = lyr?.getElement?.(); - if (element && element.classList) { - element.classList.toggle("park-selected", selected); - } + color: "#1b5e20", + weight: 4, + fillColor: "#69f0ae", + fillOpacity: 0.75, }; const NEARBY_STYLE = { - color: "#1565c0", - weight: 1.5, - fillColor: "#42a5f5", - fillOpacity: 0.25, + color: "#2e7d32", + weight: 2, + fillColor: "#66bb6a", + fillOpacity: 0.35, }; const NEARBY_POINT_STYLE = { radius: 7, - color: "#0d47a1", + color: "#1b5e20", weight: 2, - fillColor: "#42a5f5", + fillColor: "#66bb6a", fillOpacity: 0.85, }; const NEARBY_SELECTED_STYLE = { - color: "#0d47a1", - weight: 3, - fillColor: "#1565c0", - fillOpacity: 0.55, + color: "#1b5e20", + weight: 4, + fillColor: "#69f0ae", + fillOpacity: 0.75, }; const NEARBY_DIM_STYLE = { - color: "#90caf9", + color: "#c8e6c9", weight: 0.8, - fillColor: "#bbdefb", + fillColor: "#e8f5e9", fillOpacity: 0.05, - opacity: 0.3, + opacity: 0.2, + }; + + // 選択中の公園のグロー表示 + const markSelected = (lyr: any, selected: boolean) => { + const element = lyr?.getElement?.(); + if (element && element.classList) { + element.classList.toggle("park-selected", selected); + } + }; + // フィルタがないときの位置点滅 + const markBlink = (lyr: any, on: boolean) => { + const element = lyr?.getElement?.(); + if (element && element.classList) { + element.classList.toggle("park-blink", on); + } }; const el = document.getElementById("park-map"); @@ -756,6 +781,15 @@ new FullscreenControl({ position: "topright" }).addTo(map); const layersByName = new Map<string, any>(); + + // 消火栓・防火水槽はチェックオンのときだけ表示する + const fireToggle = document.querySelector<HTMLInputElement>("[data-show-fire]"); + if (fireToggle) { + const applyFire = () => + document.body.classList.toggle("show-fire", fireToggle.checked); + fireToggle.addEventListener("change", applyFire); + applyFire(); + } const allLayers: any[] = []; type NearbyEntry = { name: string; layer: any; city: string; key: string }; const nearbyEntries: NearbyEntry[] = []; @@ -797,6 +831,7 @@ let selectedBar: HTMLElement | null = null; let selectedNameEl: HTMLElement | null = null; let selectedMapsLink: HTMLAnchorElement | null = null; + let selectedRouteLink: HTMLAnchorElement | null = null; let selectedTableLink: HTMLAnchorElement | null = null; let selectedNoteEl: HTMLElement | null = null; let selectedOwnerEl: HTMLElement | null = null; @@ -827,7 +862,12 @@ rec?.["緯度"] && rec?.["経度"] ? `${rec["緯度"]},${rec["経度"]}` : query; - selectedMapsLink.href = `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(destination)}`; + selectedMapsLink.href = + rec?.["地図URL"] || + `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(query)}`; + if (selectedRouteLink) { + selectedRouteLink.href = `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(destination)}&travelmode=walking`; + } selectedTableLink.dataset.parkRow = encodeURIComponent(ourName); selectedTableLink.hidden = !listed; if (selectedNoteEl) selectedNoteEl.hidden = listed; @@ -846,11 +886,13 @@ '<span class="park-map-selected-name"></span>' + '<span class="park-map-selected-owner" hidden></span>' + '<span class="park-map-selected-note" hidden>一覧にない緑地・公園</span>' + - '<a data-park-maps-link href="#" target="_blank" rel="noopener">現在地から経路</a>' + + '<a data-park-maps-link href="#" target="_blank" rel="noopener">Googleマップ</a>' + + '<a data-park-route-link href="#" target="_blank" rel="noopener">徒歩の経路</a>' + '<a data-park-table-link href="#">一覧で見る</a>'; selectedBar = box; selectedNameEl = box.querySelector<HTMLElement>(".park-map-selected-name"); selectedMapsLink = box.querySelector<HTMLAnchorElement>("[data-park-maps-link]"); + selectedRouteLink = box.querySelector<HTMLAnchorElement>("[data-park-route-link]"); selectedTableLink = box.querySelector<HTMLAnchorElement>("[data-park-table-link]"); selectedNoteEl = box.querySelector<HTMLElement>(".park-map-selected-note"); selectedOwnerEl = box.querySelector<HTMLElement>(".park-map-selected-owner"); @@ -1182,7 +1224,7 @@ }; const updateFacilities = () => { - if (map.getZoom() >= 16) { + if (map.getZoom() >= 17) { if (!map.hasLayer(facilityLayer)) facilityLayer.addTo(map); } else if (map.hasLayer(facilityLayer)) { map.removeLayer(facilityLayer); @@ -1224,16 +1266,19 @@ if (parkName === name) { lyr.setStyle(SELECTED_STYLE); markSelected(lyr, true); + markBlink(lyr, false); lyr.bringToFront?.(); } else { lyr.setStyle(parkName ? DIM_STYLE : UNMATCHED_STYLE); markSelected(lyr, false); + markBlink(lyr, false); } }); nearbyEntries.forEach(({ name: nearbyName, layer: lyr }) => { const selected = nearbyName === name; lyr.setStyle(selected ? NEARBY_SELECTED_STYLE : NEARBY_DIM_STYLE); markSelected(lyr, selected); + markBlink(lyr, false); if (selected) lyr.bringToFront?.(); }); }; @@ -1314,10 +1359,12 @@ if (name && names.has(name)) { lyr.setStyle(SELECTED_STYLE); markSelected(lyr, true); + markBlink(lyr, false); lyr.bringToFront?.(); } else { lyr.setStyle(name ? MATCHED_STYLE : UNMATCHED_STYLE); markSelected(lyr, false); + markBlink(lyr, false); } }); nearbyEntries.forEach(({ name, layer: lyr }) => { @@ -1338,15 +1385,18 @@ if (!active) { lyr.setStyle(name ? MATCHED_STYLE : UNMATCHED_STYLE); markSelected(lyr, false); + markBlink(lyr, Boolean(name)); return; } if (name && names.has(name)) { lyr.setStyle(SELECTED_STYLE); markSelected(lyr, true); + markBlink(lyr, false); lyr.bringToFront?.(); } else { lyr.setStyle(DIM_STYLE); markSelected(lyr, false); + markBlink(lyr, false); } }); |
