Newer
Older
2025-shino / fog.js
(() => {
  'use strict';

  document.addEventListener("DOMContentLoaded", () => {

    const START_POS = [38.891, 139.824];
    const FOG_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;

    let fogLayer = null;
    const revealedHoles = [];

    function initMap() {
      map = L.map("locationmap", { tap: true })
        .setView(START_POS, 16);

      L.tileLayer("https://{s}.tile.osm.org/{z}/{x}/{y}.png", {
        attribution:
          '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
      }).addTo(map);

      playerMarker = L.marker(map.getCenter()).addTo(map);
      playerMarker.bindPopup("STARTを押して探索開始").openPopup();

      createFogLayer();

      // PC検証用
      map.on("click", (e) => {
        updateLocation(e.latlng);
      });
    }

    // 世界全体を覆う霧を作る
    function createFogLayer() {
      const world = [
        [-90, -180],
        [-90,  180],
        [ 90,  180],
        [ 90, -180]
      ];

      fogLayer = L.polygon(
        [world, ...revealedHoles],
        {
          color: "#000",
          fillColor: "#000",
          fillOpacity: 0.85,
          weight: 0
        }
      ).addTo(map);
    }

    function revealFog(latlng) {
      const points = [];
      const steps = 24;

      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);

      map.removeLayer(fogLayer);
      createFogLayer();
    }

    function updateLocation(latlng) {
      map.panTo(latlng);
      playerMarker.setLatLng(latlng);

      revealFog(latlng);

      playerMarker
        .setPopupContent("探索中…霧を解除しています")
        .openPopup();
    }

    function onSuccess(pos) {
      updateLocation(
        L.latLng(pos.coords.latitude, pos.coords.longitude)
      );
    }

    function onError() {
      playerMarker
        .setPopupContent("位置情報を取得できません")
        .openPopup();
    }

    function startWatch() {
      stopWatch();
      watchId = navigator.geolocation.watchPosition(
        onSuccess,
        onError,
        { maximumAge: 0, timeout: 3000, enableHighAccuracy: true }
      );
      titleEl.textContent = "探索中";
    }

    function stopWatch() {
      if (watchId !== null) {
        navigator.geolocation.clearWatch(watchId);
        watchId = null;
        titleEl.textContent = "STOP";
        playerMarker
          .setPopupContent("探索を停止しました")
          .openPopup();
      }
    }

    startBtn.addEventListener("click", startWatch);
    stopBtn.addEventListener("click", stopWatch);

    initMap();
  });
})();