diff --git a/examples/webpage/app.js b/examples/webpage/app.js
index ef0ed72..c6218ad 100644
--- a/examples/webpage/app.js
+++ b/examples/webpage/app.js
@@ -78,7 +78,7 @@ const els = {
cameraPhoto: document.getElementById("camera-photo"),
cameraCanvas: document.getElementById("camera-canvas"),
cameraStartBtn: document.getElementById("camera-start-btn"),
- cameraFlipBtn: document.getElementById("camera-flip-btn"),
+ cameraDeviceSelect: document.getElementById("camera-device-select"),
cameraUpload: document.getElementById("camera-upload"),
cameraSamples: document.getElementById("camera-samples"),
clearBtn: document.getElementById("clear-btn"),
@@ -149,7 +149,6 @@ const state = {
cameraActive: false,
cameraFacing: "environment",
videoDevices: [],
- videoDeviceIndex: 0,
pendingImage: null,
samplesRendered: false,
@@ -258,6 +257,9 @@ function syncCameraDrawer(value) {
els.cameraQuestion.textContent = prompt;
renderSampleThumbnails();
selectDefaultImage();
+ refreshVideoDevices().then(() => {
+ if (!state.cameraActive) populateDeviceSelect();
+ });
} else {
els.cameraState.textContent = "State -";
els.cameraQuestion.textContent = "";
@@ -924,6 +926,30 @@ async function refreshVideoDevices() {
}
}
+// Fill the camera dropdown from the enumerated devices. Labels are only exposed
+// after camera permission has been granted, so before that we show generic
+// names ("摄像头 1", …) or just the default option.
+function populateDeviceSelect(activeDeviceId) {
+ const sel = els.cameraDeviceSelect;
+ sel.innerHTML = "";
+ if (state.videoDevices.length === 0) {
+ const opt = document.createElement("option");
+ opt.value = "";
+ opt.textContent = "默认摄像头";
+ sel.appendChild(opt);
+ sel.disabled = true;
+ return;
+ }
+ state.videoDevices.forEach((device, index) => {
+ const opt = document.createElement("option");
+ opt.value = device.deviceId;
+ opt.textContent = device.label || `摄像头 ${index + 1}`;
+ sel.appendChild(opt);
+ });
+ sel.disabled = false;
+ if (activeDeviceId) sel.value = activeDeviceId;
+}
+
async function startCamera(deviceId) {
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
addWsLog("system", "getUserMedia not available in this browser");
@@ -954,31 +980,16 @@ async function startCamera(deviceId) {
els.cameraStartBtn.classList.add("is-active");
clearSampleSelection();
- // Device labels become available only after permission is granted.
+ // Device labels become available only after permission is granted; refresh
+ // the dropdown now and select whichever camera is actually streaming.
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;
+ const activeId =
+ state.cameraStream.getVideoTracks?.()[0]?.getSettings?.().deviceId ||
+ deviceId;
+ populateDeviceSelect(activeId);
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());
@@ -987,7 +998,6 @@ function stopCameraStream() {
els.cameraVideo.srcObject = null;
state.cameraActive = false;
els.cameraStartBtn.classList.remove("is-active");
- els.cameraFlipBtn.disabled = true;
}
function captureFromCamera() {
@@ -1456,11 +1466,15 @@ els.cameraDoneBtn.addEventListener("click", () => {
});
els.cameraStartBtn.addEventListener("click", () => {
- startCamera();
+ startCamera(els.cameraDeviceSelect.value || undefined);
});
-els.cameraFlipBtn.addEventListener("click", () => {
- switchCamera();
+els.cameraDeviceSelect.addEventListener("change", () => {
+ // Switching device only restarts the stream when the camera is already live;
+ // otherwise the choice is applied when "使用摄像头" is pressed.
+ if (state.cameraActive) {
+ startCamera(els.cameraDeviceSelect.value || undefined);
+ }
});
els.cameraUpload.addEventListener("change", (event) => {
diff --git a/examples/webpage/index.html b/examples/webpage/index.html
index 0330f35..3c1c1c9 100644
--- a/examples/webpage/index.html
+++ b/examples/webpage/index.html
@@ -116,14 +116,14 @@
>
使用摄像头
-
+
+