Newer
Older
pj25dx-d / kakunin / figma.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.tailwindcss.com"></script>
<title>Camera Figma Exact</title>
</head>

<body class="flex justify-center bg-gray-100 py-10">

<div class="w-96 h-[852px] relative bg-gradient-to-l from-cyan-200 to-indigo-500 overflow-hidden">

<!-- 投稿する -->
<div id="submitPost"
  class="w-96 h-16 left-[12px] top-[691px] absolute bg-white border-[3px] border-black cursor-pointer">
</div>
<div class="w-96 h-5 left-[19px] top-[714px] absolute text-center text-black text-xl pointer-events-none">
投稿する
</div>

<!-- 場所 -->
<div class="w-64 h-16 left-[128px] top-[587px] absolute bg-white border-[3px] border-black"></div>
<div class="w-32 h-12 left-[4px] top-[592px] absolute text-black text-2xl font-bold">
場所
</div>

<input id="locationInput"
  class="w-60 h-16 left-[135px] top-[587px] absolute bg-transparent text-black text-2xl font-bold outline-none"
  placeholder="直接入力もできます"
/>

<select id="locationSelect"
  class="w-60 h-16 left-[135px] top-[587px] absolute bg-transparent text-transparent outline-none">
  <option>周辺の場所を取得中...</option>
</select>

<!-- 撮影日時 -->
<div class="w-64 h-16 left-[128px] top-[508px] absolute bg-white border-[3px] border-black"></div>
<input id="dateInput"
  type="date"
  class="w-60 h-16 left-[133px] top-[508px] absolute bg-transparent text-black text-2xl font-bold outline-none"
/>
<div class="w-32 h-12 left-[4px] top-[513px] absolute text-black text-2xl font-bold">
撮影日時
</div>

<!-- カメラ -->
<div class="w-80 h-60 left-[35.26px] top-[216px] absolute bg-white border-[3px] border-black overflow-hidden">
<video id="video" class="w-full h-full object-cover hidden" autoplay playsinline muted></video>
</div>

<div id="startCamera"
  class="w-80 h-20 left-[41.51px] top-[306.52px] absolute text-center text-black text-2xl font-bold cursor-pointer select-none">
カメラを起動
</div>

<!-- ミッション -->
<div class="w-96 h-28 left-[12px] top-[51px] absolute bg-sky-950 rounded-[20px] border-[5px] border-stone-400"></div>
<div class="w-80 h-24 left-[25px] top-[64px] absolute text-center text-amber-400 text-3xl">
酒田の大獅子を<br>撮影せよ!
</div>

</div>

<script>
/* 日付 */
dateInput.value = new Date().toISOString().split("T")[0];

/* ===== カメラ(PC・スマホ確実対応) ===== */
startCamera.onclick = async () => {
  if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
    alert("このブラウザはカメラに対応していません");
    return;
  }

  if (location.protocol !== "https:" && location.hostname !== "localhost") {
    alert("カメラはHTTPS環境でのみ使用できます");
    return;
  }

  try {
    const stream = await navigator.mediaDevices.getUserMedia({
      video: {
        facingMode: { ideal: "environment" },
        width: { ideal: 1280 },
        height: { ideal: 720 }
      }
    });

    video.srcObject = stream;
    video.classList.remove("hidden");
    startCamera.classList.add("hidden");
  } catch (e) {
    alert("カメラの使用が許可されていないか、利用できません");
  }
};

/* ===== 周辺検索(失敗時フォールバック付き) ===== */
async function loadNearbyPlaces() {
  if (!navigator.geolocation) {
    locationSelect.innerHTML = `<option>位置情報非対応</option>`;
    return;
  }

  navigator.geolocation.getCurrentPosition(async pos => {
    const { latitude, longitude } = pos.coords;

    try {
      const query = `
      [out:json][timeout:10];
      (
        node(around:1000,${latitude},${longitude})["name"];
      );
      out tags 20;
      `;

      const res = await fetch("https://overpass-api.de/api/interpreter", {
        method: "POST",
        body: query
      });

      if (!res.ok) throw new Error();

      const data = await res.json();

      locationSelect.innerHTML = `<option value="">場所を選択</option>`;

      let count = 0;
      data.elements.forEach(e => {
        if (e.tags?.name && count < 20) {
          const opt = document.createElement("option");
          opt.value = e.tags.name;
          opt.textContent = e.tags.name;
          locationSelect.appendChild(opt);
          count++;
        }
      });

      if (count === 0) throw new Error();

    } catch {
      locationSelect.innerHTML = `<option value="">検索失敗(手入力してください)</option>`;
    }
  }, () => {
    locationSelect.innerHTML = `<option value="">位置情報が拒否されました</option>`;
  });
}

loadNearbyPlaces();

/* 選択 → 入力欄へ */
locationSelect.onchange = () => {
  if (locationSelect.value) {
    locationInput.value = locationSelect.value;
  }
};

/* 投稿 */
submitPost.onclick = () => {
  if (!locationInput.value) {
    alert("場所を入力または選択してください");
    return;
  }
  alert(`投稿\n場所:${locationInput.value}\n日時:${dateInput.value}`);
};
</script>

</body>
</html>