diff --git a/fog.js b/fog.js index 27e94ce..c833708 100644 --- a/fog.js +++ b/fog.js @@ -3,144 +3,108 @@ document.addEventListener("DOMContentLoaded", () => { - // ===== 設定 ===== const START_POS = [38.891, 139.824]; - const FOG_RADIUS = 40; // 見える半径(m) - const GRID_SIZE = 20; // 解除単位(m) + const REVEAL_RADIUS = 40; // m - // ===== DOM ===== const titleEl = document.getElementById("title"); const startBtn = document.getElementById("start"); const stopBtn = document.getElementById("stop"); - // ===== 状態 ===== let map; let playerMarker; let watchId = null; - let fogLayer = null; - const revealedHoles = []; + // === 解除済みポイント(世界座標)=== + const revealedPoints = []; - // 解除済みグリッド - const revealedGrid = new Set(); + // === 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; + + 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", { tap: true }) - .setView(START_POS, 16); + map = L.map("locationmap").setView(START_POS, 16); L.tileLayer("https://{s}.tile.osm.org/{z}/{x}/{y}.png", { - attribution: - '© OpenStreetMap contributors' + attribution: '© OpenStreetMap contributors' }).addTo(map); + fogLayer = new FogLayer(); + fogLayer.addTo(map); + playerMarker = L.marker(map.getCenter()).addTo(map); - playerMarker.bindPopup("STARTを押して探索開始").openPopup(); + playerMarker.bindPopup("STARTで探索開始").openPopup(); - createFogLayer(); - - // PC検証用 - map.on("click", (e) => { - updateLocation(e.latlng); - }); + map.on("click", e => updateLocation(e.latlng)); } - // ===== 世界を覆う霧 ===== - function createFogLayer() { - const world = [ - [-90, -180], - [-90, 180], - [ 90, 180], - [ 90, -180] - ]; - - if (fogLayer) map.removeLayer(fogLayer); - - fogLayer = L.polygon( - [world, ...revealedHoles], - { - fillColor: "#000", - fillOpacity: 0.85, - weight: 0 - } - ).addTo(map); + function reveal(latlng) { + revealedPoints.push(latlng); + fogLayer.redraw(); // ← ここが重要 } - // ===== グリッドID計算 ===== - function gridId(latlng) { - const latSize = GRID_SIZE / 111320; - const lngSize = GRID_SIZE / (111320 * Math.cos(latlng.lat * Math.PI / 180)); - - const gx = Math.floor(latlng.lng / lngSize); - const gy = Math.floor(latlng.lat / latSize); - - return `${gx}_${gy}`; - } - - // ===== 霧解除 ===== - function revealFog(latlng) { - const id = gridId(latlng); - - // すでに解除済み → 何もしない - if (revealedGrid.has(id)) return; - - revealedGrid.add(id); - - const points = []; - const steps = 16; - - for (let i = 0; i < steps; i++) { - const angle = (i / steps) * Math.PI * 2; - const dx = (FOG_RADIUS / 111320) * Math.cos(angle); - const dy = (FOG_RADIUS / 111320) * Math.sin(angle); - - points.push([ - latlng.lat + dy, - latlng.lng + dx - ]); - } - - revealedHoles.push(points); - createFogLayer(); - } - - // ===== 位置更新 ===== function updateLocation(latlng) { map.panTo(latlng); playerMarker.setLatLng(latlng); + reveal(latlng); - revealFog(latlng); - - playerMarker - .setPopupContent("探索中…") - .openPopup(); + playerMarker.setPopupContent("探索中…").openPopup(); } - // ===== Geolocation ===== function onSuccess(pos) { - updateLocation( - L.latLng(pos.coords.latitude, pos.coords.longitude) - ); - } - - function onError() { - playerMarker - .setPopupContent("位置情報を取得できません") - .openPopup(); + updateLocation(L.latLng(pos.coords.latitude, pos.coords.longitude)); } function startWatch() { stopWatch(); watchId = navigator.geolocation.watchPosition( onSuccess, - onError, - { maximumAge: 0, timeout: 3000, enableHighAccuracy: true } + null, + { enableHighAccuracy: true } ); titleEl.textContent = "探索中"; } function stopWatch() { - if (watchId !== null) { + if (watchId) { navigator.geolocation.clearWatch(watchId); watchId = null; titleEl.textContent = "STOP";