ЧЗ scanner: zoom + torch, looser ROI, CSP wasm-unsafe-eval (wasm verified on device)
- The tight guide box forced the phone closer than it can focus. Drop it: decode
the whole visible center square (WYSIWYG under object-fit:cover) at up to 768px;
zxing-wasm locates the code anywhere in it.
- Add a zoom slider and a torch toggle driven by MediaStreamTrack capabilities
(getCapabilities/applyConstraints), plus continuous focus and a 1920x1080 request
— the user enlarges a small code optically instead of moving too close.
- CSP: add "script-src ... 'wasm-unsafe-eval'". Tauri's generated hash-based
script-src dropped unsafe-eval, so WebAssembly.instantiate was blocked
("violates CSP") — verified fixed on-device via CDP (zxReady ok, code_33 decodes).
This commit is contained in:
parent
5f30af1cf7
commit
0e44dc07e2
2 changed files with 54 additions and 7 deletions
|
|
@ -1864,7 +1864,12 @@ function showChznScanModal(onSaved) {
|
|||
<h3>Скан «Честный знак»</h3>
|
||||
<div style="position:relative;background:#000;border-radius:8px;overflow:hidden;aspect-ratio:1/1;max-height:60vh">
|
||||
<video id="cz-video" playsinline muted style="width:100%;height:100%;object-fit:cover"></video>
|
||||
<div style="position:absolute;inset:14%;border:2px solid rgba(255,255,255,.7);border-radius:8px;pointer-events:none"></div>
|
||||
<div style="position:absolute;inset:4%;border:1px solid rgba(255,255,255,.35);border-radius:8px;pointer-events:none"></div>
|
||||
<button id="cz-torch" class="btn-secondary" style="display:none;position:absolute;top:8px;right:8px;padding:6px 10px;font-size:16px;line-height:1">💡</button>
|
||||
</div>
|
||||
<div id="cz-zoom-wrap" style="display:none;align-items:center;gap:10px;margin-top:10px">
|
||||
<span style="font-size:16px">🔍</span>
|
||||
<input id="cz-zoom" type="range" style="flex:1" />
|
||||
</div>
|
||||
<div id="cz-status" style="margin:10px 0;font-size:13px;color:var(--muted);min-height:18px">Запуск камеры...</div>
|
||||
<div style="display:flex;gap:8px">
|
||||
|
|
@ -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) + "). Выберите фото или введите код.";
|
||||
|
|
|
|||
|
|
@ -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": [
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue