diff --git a/frontend/js/main.js b/frontend/js/main.js index 02d42c2..32d2b54 100644 --- a/frontend/js/main.js +++ b/frontend/js/main.js @@ -1864,7 +1864,12 @@ function showChznScanModal(onSaved) {

Скан «Честный знак»

-
+
+ +
+
Запуск камеры...
@@ -1920,9 +1925,13 @@ function showChznScanModal(onSaved) { function grabROI() { let vw = video.videoWidth, vh = video.videoHeight; if (!vw || !vh) return null; - let side = Math.min(vw, vh) * 0.72; + // The square container shows the video center-cropped (object-fit:cover), so + // sample exactly that center square (WYSIWYG). zxing-wasm locates the code + // anywhere within it — no need for the user to fill a tiny box; use zoom to + // enlarge the code at a focusable distance instead. + let side = Math.min(vw, vh); let sx = (vw - side) / 2, sy = (vh - side) / 2; - let out = 540; + let out = Math.min(side, 768); // enough detail, still fast canvas.width = out; canvas.height = out; cctx.drawImage(video, sx, sy, side, side, 0, 0, out, out); return cctx.getImageData(0, 0, out, out); @@ -1940,6 +1949,41 @@ function showChznScanModal(onSaved) { if (scanning) setTimeout(tick, 100); } + // Wire zoom slider / torch / continuous focus from the track capabilities. + // Zoom is the real answer to "the code is too small at focus distance" — the + // user enlarges the code optically instead of moving the phone dangerously close. + let torchOn = false; + function setupCameraControls() { + let track = stream && stream.getVideoTracks && stream.getVideoTracks()[0]; + if (!track || !track.getCapabilities) return; + let caps = {}; + try { caps = track.getCapabilities() || {}; } catch {} + try { + if (caps.focusMode && caps.focusMode.includes("continuous")) { + track.applyConstraints({ advanced: [{ focusMode: "continuous" }] }).catch(() => {}); + } + } catch {} + if (caps.zoom && caps.zoom.max > caps.zoom.min) { + let zw = ov.querySelector("#cz-zoom-wrap"); + let zi = ov.querySelector("#cz-zoom"); + zi.min = caps.zoom.min; zi.max = caps.zoom.max; + zi.step = caps.zoom.step || (caps.zoom.max - caps.zoom.min) / 100 || 0.1; + let cur = ((track.getSettings && track.getSettings()) || {}).zoom; + zi.value = cur || caps.zoom.min; + zi.oninput = () => { track.applyConstraints({ advanced: [{ zoom: parseFloat(zi.value) }] }).catch(() => {}); }; + zw.style.display = "flex"; + } + if (caps.torch) { + let tb = ov.querySelector("#cz-torch"); + tb.style.display = "block"; + tb.onclick = () => { + torchOn = !torchOn; + track.applyConstraints({ advanced: [{ torch: torchOn }] }).catch(() => {}); + tb.style.background = torchOn ? "rgba(255,214,10,.35)" : ""; + }; + } + } + async function startCamera() { try { statusEl.textContent = "Загрузка декодера..."; @@ -1949,11 +1993,14 @@ function showChznScanModal(onSaved) { return; } try { - stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: { ideal: "environment" } } }); + stream = await navigator.mediaDevices.getUserMedia({ + video: { facingMode: { ideal: "environment" }, width: { ideal: 1920 }, height: { ideal: 1080 } }, + }); video.srcObject = stream; await video.play(); + setupCameraControls(); scanning = true; - statusEl.textContent = "Наведите на код Честного знака..."; + statusEl.textContent = "Наведите на код (используйте зум, если код мелкий)…"; tick(); } catch (e) { statusEl.textContent = "Камера недоступна (" + (e.name || e.message) + "). Выберите фото или введите код."; diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index f5ca4c9..1d96388 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/nicegui/tauri-v2/refs/heads/main/tooling/cli/schema.json", "productName": "BalDav", - "version": "0.1.423", + "version": "0.1.426", "identifier": "dev.baldav.app", "build": { "frontendDist": "../frontend" @@ -9,7 +9,7 @@ "app": { "withGlobalTauri": true, "security": { - "csp": "default-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src 'self' https://4aiman.top https://*.tile.openstreetmap.org https://*.tile.thunderforest.com https://api.open-meteo.com https://wttr.in http://192.168.1.1:88; img-src 'self' blob: data: https://*.tile.openstreetmap.org https://*.tile.thunderforest.com" + "csp": "default-src 'self' 'unsafe-inline' 'unsafe-eval'; script-src 'self' 'wasm-unsafe-eval' 'unsafe-eval' 'unsafe-inline'; connect-src 'self' https://4aiman.top https://*.tile.openstreetmap.org https://*.tile.thunderforest.com https://api.open-meteo.com https://wttr.in http://192.168.1.1:88; img-src 'self' blob: data: https://*.tile.openstreetmap.org https://*.tile.thunderforest.com" }, "windows": [ {