diff --git a/examples/webpage/app.js b/examples/webpage/app.js index 141d61c..ef0ed72 100644 --- a/examples/webpage/app.js +++ b/examples/webpage/app.js @@ -148,6 +148,8 @@ const state = { cameraStream: null, cameraActive: false, cameraFacing: "environment", + videoDevices: [], + videoDeviceIndex: 0, pendingImage: null, samplesRendered: false, @@ -255,6 +257,7 @@ function syncCameraDrawer(value) { els.cameraState.textContent = `State ${value}`; els.cameraQuestion.textContent = prompt; renderSampleThumbnails(); + selectDefaultImage(); } else { els.cameraState.textContent = "State -"; els.cameraQuestion.textContent = ""; @@ -912,15 +915,27 @@ function setPendingImage(payload) { setCameraButtonEnabled(); } -async function startCamera() { +async function refreshVideoDevices() { + try { + const devices = await navigator.mediaDevices.enumerateDevices(); + state.videoDevices = devices.filter((d) => d.kind === "videoinput"); + } catch (_) { + state.videoDevices = []; + } +} + +async function startCamera(deviceId) { if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { addWsLog("system", "getUserMedia not available in this browser"); return; } stopCameraStream(); + const video = deviceId + ? { deviceId: { exact: deviceId } } + : { facingMode: state.cameraFacing }; try { state.cameraStream = await navigator.mediaDevices.getUserMedia({ - video: { facingMode: state.cameraFacing }, + video, audio: false, }); } catch (err) { @@ -937,12 +952,33 @@ async function startCamera() { state.pendingImage = null; setPreviewMode("camera"); els.cameraStartBtn.classList.add("is-active"); - els.cameraStartBtn.textContent = "重新拍摄"; - els.cameraFlipBtn.hidden = false; clearSampleSelection(); + + // Device labels become available only after permission is granted. + await refreshVideoDevices(); + if (deviceId) { + const idx = state.videoDevices.findIndex((d) => d.deviceId === deviceId); + if (idx >= 0) state.videoDeviceIndex = idx; + } + els.cameraFlipBtn.disabled = state.videoDevices.length < 2; setCameraButtonEnabled(); } +// Cycle to the next available camera, or flip facingMode when devices are +// unlabeled (e.g. before permission, or on mobile with front/back only). +async function switchCamera() { + if (!state.cameraActive) return; + if (state.videoDevices.length > 1) { + state.videoDeviceIndex = + (state.videoDeviceIndex + 1) % state.videoDevices.length; + await startCamera(state.videoDevices[state.videoDeviceIndex].deviceId); + } else { + state.cameraFacing = + state.cameraFacing === "environment" ? "user" : "environment"; + await startCamera(); + } +} + function stopCameraStream() { if (state.cameraStream) { state.cameraStream.getTracks().forEach((track) => track.stop()); @@ -951,8 +987,7 @@ function stopCameraStream() { els.cameraVideo.srcObject = null; state.cameraActive = false; els.cameraStartBtn.classList.remove("is-active"); - els.cameraStartBtn.textContent = "使用摄像头"; - els.cameraFlipBtn.hidden = true; + els.cameraFlipBtn.disabled = true; } function captureFromCamera() { @@ -1037,6 +1072,16 @@ function resetCameraInput() { setCameraButtonEnabled(); } +// Pre-select the first sample image so "拍摄完成" is immediately pressable when +// the drawer opens, without requiring the user to capture or pick first. +function selectDefaultImage() { + if (state.pendingImage || state.cameraActive) return; + const first = els.cameraSamples.querySelector(".camera-drawer__sample"); + if (first && SAMPLE_IMAGES[0]) { + selectSampleImage(SAMPLE_IMAGES[0].src, first); + } +} + function sendImage(payload, text) { if (!payload) return false; if (!state.ws || state.ws.readyState !== WebSocket.OPEN) return false; @@ -1415,9 +1460,7 @@ els.cameraStartBtn.addEventListener("click", () => { }); els.cameraFlipBtn.addEventListener("click", () => { - state.cameraFacing = - state.cameraFacing === "environment" ? "user" : "environment"; - if (state.cameraActive) startCamera(); + switchCamera(); }); els.cameraUpload.addEventListener("change", (event) => { diff --git a/examples/webpage/index.html b/examples/webpage/index.html index aa14030..0330f35 100644 --- a/examples/webpage/index.html +++ b/examples/webpage/index.html @@ -120,11 +120,13 @@ id="camera-flip-btn" class="btn btn--ghost camera-drawer__source" type="button" - hidden + disabled > 切换摄像头 -