From 39bef28ad58461f27925ba99349e513907e34dd0 Mon Sep 17 00:00:00 2001 From: Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> Date: Sat, 7 Nov 2026 10:54:59 +0900 Subject: parks: 公園クリックをフキダシ表示にし、ズームと点滅を調整 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 公園クリックで詳細のフキダシを表示し、×や外側クリックで閉じるようにした。他の公園は点滅を維持。点滅を強め、名前をアイコンの下に置き、遊具アイコンをズーム16から表示。全画面は地図をbody直下に移してiPhoneでも全画面になるよう修正。遊具ありフィルタを削除し、新フィルタにアイコンを追加。 --- src/components/ParkMap.astro | 168 +++++++++++++++++++++++++++++++++---------- 1 file changed, 132 insertions(+), 36 deletions(-) (limited to 'src/components/ParkMap.astro') diff --git a/src/components/ParkMap.astro b/src/components/ParkMap.astro index c67b4b3..8e7f35c 100644 --- a/src/components/ParkMap.astro +++ b/src/components/ParkMap.astro @@ -33,14 +33,17 @@ border-radius: 0; } .park-map.is-maximized { - position: fixed; - inset: 0; - width: 100vw; - height: 100dvh; - margin: 0; - border: 0; - border-radius: 0; - z-index: 10000; + position: fixed !important; + inset: 0 !important; + width: auto !important; + height: auto !important; + max-width: none !important; + max-height: none !important; + margin: 0 !important; + border: 0 !important; + border-radius: 0 !important; + transform: none !important; + z-index: 2147483000 !important; } :global(body.park-map-maximized) { overflow: hidden; @@ -116,6 +119,42 @@ font-size: 0.78rem; cursor: pointer; } + :global(.park-popup) { + font-size: 0.82rem; + line-height: 1.5; + color: #1e293b; + } + :global(.park-popup strong) { + font-size: 0.95rem; + } + :global(.park-popup-owner) { + margin-left: 0.4rem; + font-size: 0.7rem; + color: #555; + } + :global(.park-popup-meta) { + margin-top: 0.15rem; + color: #444; + font-size: 0.78rem; + } + :global(.park-popup-fac) { + margin-top: 0.1rem; + color: #2e7d32; + font-size: 0.78rem; + } + :global(.park-popup-links) { + display: flex; + gap: 0.8rem; + margin-top: 0.35rem; + } + :global(.park-popup-links a) { + display: inline-flex; + align-items: center; + gap: 0.2rem; + color: #0b57d0; + font-weight: 700; + } + :global(.park-name-marker) { background: none; border: 0; @@ -124,7 +163,7 @@ position: absolute; left: 0; top: 0; - transform: translate(-50%, -170%); + transform: translate(-50%, 60%); background: rgba(255, 255, 255, 0.92); color: #1b5e20; font-size: 11px; @@ -186,10 +225,12 @@ @keyframes park-blink-pulse { 0%, 100% { - fill-opacity: 0.45; + fill-opacity: 0.6; + stroke-opacity: 1; } 50% { - fill-opacity: 0.12; + fill-opacity: 0.08; + stroke-opacity: 0.3; } } @media (prefers-reduced-motion: reduce) { @@ -838,7 +879,12 @@ window.setTimeout(() => map.invalidateSize(), 60); window.setTimeout(() => map.invalidateSize(), 350); }; + let maxHome: { parent: Node; next: Node | null } | null = null; const enterCssMax = () => { + if (!maxHome) { + maxHome = { parent: target.parentNode as Node, next: target.nextSibling }; + document.body.appendChild(target); + } target.classList.add("is-maximized"); document.body.classList.add("park-map-maximized"); update(); @@ -846,6 +892,10 @@ const exitCssMax = () => { target.classList.remove("is-maximized"); document.body.classList.remove("park-map-maximized"); + if (maxHome) { + maxHome.parent.insertBefore(target, maxHome.next); + maxHome = null; + } update(); }; @@ -917,6 +967,40 @@ ? "民間" : owner; + const escapePopup = (value: string) => + value.replace(/[&<>"']/g, (ch) => + ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[ch] as string, + ); + const buildPopup = (ourName: string, osmName: string, owner = "", mapsQuery = "") => { + const rec = parkDataByName.get(ourName); + const name = escapePopup(ourName || osmName || ""); + const addr = escapePopup(rec?.["住所"] || ""); + const areaRaw = Number(rec?.["面積㎡"]); + const area = + Number.isFinite(areaRaw) && areaRaw > 0 + ? `${areaRaw.toLocaleString("ja-JP")}㎡` + : ""; + const type = escapePopup(rec?.["種別"] || ""); + const facs = [ + rec?.["トイレ"] ? "トイレ" : "", + rec?.["水飲み場"] ? "水飲み" : "", + rec?.["遊具"] ? "遊具" : "", + rec?.["シェルター等"] ? "東屋等" : "", + ] + .filter(Boolean) + .join("・"); + const query = mapsQuery || (ourName ? `小平市${ourName}` : ""); + const dest = + rec?.["緯度"] && rec?.["経度"] + ? `${rec["緯度"]},${rec["経度"]}` + : query; + const maps = + rec?.["地図URL"] || + `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(query)}`; + const route = `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(dest)}&travelmode=walking`; + return `
`; + }; + const filterName = (name: string, scroll = false) => { document.dispatchEvent( new CustomEvent("park:filter-name", { detail: { name, scroll } }), @@ -1024,13 +1108,25 @@ const path = lyr.getElement?.(); if (path) path.setAttribute("data-park", ourName); }); + lyr.bindPopup(buildPopup(ourName, name, ownerByName.get(ourName) || ""), { + maxWidth: 300, + }); + lyr.on("popupopen", () => { + lyr.setStyle(SELECTED_STYLE); + markSelected(lyr, true); + markBlink(lyr, false); + lyr.bringToFront?.(); + }); + lyr.on("popupclose", () => { + lyr.setStyle(MATCHED_STYLE); + markSelected(lyr, false); + markBlink(lyr, true); + }); lyr.on("click", (ev: any) => { if (ev?.originalEvent) L.DomEvent.stopPropagation(ev.originalEvent); - highlightOnly(ourName); document.dispatchEvent( new CustomEvent("park:select-row", { detail: { name: ourName } }), ); - showSelected(ourName, name, true, ownerByName.get(ourName) || ""); }); } else { // 一覧にない公園・緑地は表示だけ。クリックには反応させない。 @@ -1097,14 +1193,28 @@ const key = `${p.osm_type || ""}/${p.osm_id || ""}`; lyr._isPoint = feature.geometry?.type === "Point"; if (name) nearbyEntries.push({ name, layer: lyr, city, key }); + if (name) { + lyr.bindPopup(buildPopup(name, name, "", nearbyMapsQuery(name, city)), { + maxWidth: 300, + }); + lyr.on("popupopen", () => { + lyr.setStyle(NEARBY_SELECTED_STYLE); + markSelected(lyr, true); + markBlink(lyr, false); + lyr.bringToFront?.(); + }); + lyr.on("popupclose", () => { + lyr.setStyle(nearbyBaseStyle(lyr)); + markSelected(lyr, false); + markBlink(lyr, true); + }); + } lyr.on("click", (ev: any) => { if (ev?.originalEvent) L.DomEvent.stopPropagation(ev.originalEvent); if (!name) return; - highlightOnly(name); document.dispatchEvent( new CustomEvent("park:select-row", { detail: { name } }), ); - showSelected(name, name, true, city || "市外", nearbyMapsQuery(name, city)); }); }, }).addTo(map); @@ -1339,12 +1449,12 @@ let facilityLabelsShown = false; const updateFacilities = () => { - if (map.getZoom() >= 17) { + if (map.getZoom() >= 16) { if (!map.hasLayer(facilityLayer)) facilityLayer.addTo(map); } else if (map.hasLayer(facilityLayer)) { map.removeLayer(facilityLayer); } - if (map.getZoom() >= 17) { + if (map.getZoom() >= 16) { if (!map.hasLayer(playLayer)) playLayer.addTo(map); } else if (map.hasLayer(playLayer)) { map.removeLayer(playLayer); @@ -1431,27 +1541,16 @@ if (nearbyHit) { const group = L.featureGroup([nearbyHit.layer]); map.fitBounds(group.getBounds(), { padding: [40, 40], maxZoom: 17 }); - highlightOnly(name); - showSelected( - name, - name, - true, - nearbyHit.city || "市外", - nearbyMapsQuery(name, nearbyHit.city), - ); + nearbyHit.layer.openPopup?.(); return true; } } const target = layersByName.get(name); if (target) { map.fitBounds(target.getBounds(), { padding: [40, 40], maxZoom: 17 }); - highlightOnly(name); - showSelected( - name, - target._osmName || name, - true, - ownerByName.get(name) || "", - ); + const targetLayer = + typeof target.getLayers === "function" ? target.getLayers()[0] : target; + targetLayer?.openPopup?.(); return true; } const nearbyTargets = nearbyEntries @@ -1460,10 +1559,7 @@ if (nearbyTargets.length) { const group = L.featureGroup(nearbyTargets); map.fitBounds(group.getBounds(), { padding: [40, 40], maxZoom: 17 }); - highlightOnly(name); - const info = nearbyInfoByName.get(name); - const city = String(info?.city || "").trim(); - showSelected(name, name, true, city || "市外", nearbyMapsQuery(name, city)); + nearbyTargets[0].openPopup?.(); return true; } return false; -- cgit v1.3.1