diff --git a/fog.js b/fog.js index 2b8997a..c833708 100644 --- a/fog.js +++ b/fog.js @@ -4,16 +4,61 @@ document.addEventListener("DOMContentLoaded", () => { const START_POS = [38.891, 139.824]; - const REVEAL_RADIUS = 40; // メートル - const GRID_SIZE = 0.0005; // 霧管理用グリッド + const REVEAL_RADIUS = 40; // m + + const titleEl = document.getElementById("title"); + const startBtn = document.getElementById("start"); + const stopBtn = document.getElementById("stop"); let map; let playerMarker; + let watchId = null; + + // === 解除済みポイント(世界座標)=== + const revealedPoints = []; + + // === Canvas霧レイヤ === + const FogLayer = L.GridLayer.extend({ + createTile: function (coords) { + const tile = document.createElement('canvas'); + const size = this.getTileSize(); + tile.width = size.x; + tile.height = size.y; + + const ctx = tile.getContext('2d'); + + // 霧を塗る + ctx.fillStyle = 'rgba(0,0,0,0.85)'; + ctx.fillRect(0, 0, size.x, size.y); + + // タイル境界の緯度経度 + const bounds = this._tileCoordsToBounds(coords); + + // 解除ポイントを削る + revealedPoints.forEach(latlng => { + if (!bounds.contains(latlng)) return; + + const point = map.latLngToContainerPoint(latlng); + const tilePoint = map.latLngToContainerPoint(bounds.getNorthWest()); + + const x = point.x - tilePoint.x; + const y = point.y - tilePoint.y; + + ctx.globalCompositeOperation = 'destination-out'; + ctx.beginPath(); + ctx.arc(x, y, metersToPixels(REVEAL_RADIUS, latlng.lat), 0, Math.PI * 2); + ctx.fill(); + }); + + return tile; + } + }); + let fogLayer; - // 探索済みグリッド管理 - const revealedCells = new Set(); - const holes = []; + function metersToPixels(m, lat) { + return m / 40075017 * 256 * Math.pow(2, map.getZoom()) / Math.cos(lat * Math.PI / 180); + } function initMap() { map = L.map("locationmap").setView(START_POS, 16); @@ -22,83 +67,53 @@ attribution: '© OpenStreetMap contributors' }).addTo(map); - playerMarker = L.marker(START_POS).addTo(map); + fogLayer = new FogLayer(); + fogLayer.addTo(map); - createFog(); + playerMarker = L.marker(map.getCenter()).addTo(map); + playerMarker.bindPopup("STARTで探索開始").openPopup(); - map.on("click", (e) => { - updateLocation(e.latlng); - }); + map.on("click", e => updateLocation(e.latlng)); } - /** ===== 霧を1回だけ作成 ===== */ - function createFog() { - const world = [ - [-90, -180], - [-90, 180], - [90, 180], - [90, -180] - ]; - - fogLayer = L.polygon([world], { - color: "#000", - fillColor: "#000", - fillOpacity: 0.6, - stroke: false - }).addTo(map); + function reveal(latlng) { + revealedPoints.push(latlng); + fogLayer.redraw(); // ← ここが重要 } - /** ===== 位置更新 ===== */ function updateLocation(latlng) { + map.panTo(latlng); playerMarker.setLatLng(latlng); - revealFog(latlng); + reveal(latlng); + + playerMarker.setPopupContent("探索中…").openPopup(); } - /** ===== 霧解除(チカチカしない核心部分) ===== */ - function revealFog(latlng) { - const key = gridKey(latlng); - - // すでに解除済みなら何もしない - if (revealedCells.has(key)) return; - - revealedCells.add(key); - - const hole = createCircle(latlng, REVEAL_RADIUS); - holes.push(hole); - - // 🔑 ポリゴンは作り直さず、穴だけ更新 - fogLayer.setLatLngs([ - fogLayer.getLatLngs()[0], // 外枠 - ...holes - ]); + function onSuccess(pos) { + updateLocation(L.latLng(pos.coords.latitude, pos.coords.longitude)); } - /** ===== グリッド判定 ===== */ - function gridKey(latlng) { - const x = Math.floor(latlng.lat / GRID_SIZE); - const y = Math.floor(latlng.lng / GRID_SIZE); - return `${x}_${y}`; + function startWatch() { + stopWatch(); + watchId = navigator.geolocation.watchPosition( + onSuccess, + null, + { enableHighAccuracy: true } + ); + titleEl.textContent = "探索中"; } - /** ===== 円形穴生成 ===== */ - function createCircle(center, radius) { - const points = []; - const steps = 24; - - for (let i = 0; i < steps; i++) { - const angle = (i / steps) * Math.PI * 2; - const dx = (radius * Math.cos(angle)) / 111320; - const dy = (radius * Math.sin(angle)) / - (111320 * Math.cos(center.lat * Math.PI / 180)); - - points.push([ - center.lat + dy, - center.lng + dx - ]); + function stopWatch() { + if (watchId) { + navigator.geolocation.clearWatch(watchId); + watchId = null; + titleEl.textContent = "STOP"; } - return points; } + startBtn.addEventListener("click", startWatch); + stopBtn.addEventListener("click", stopWatch); + initMap(); }); })();