ЧЗ scanner: digital-zoom fallback + fix back-stack desync on scan->add
- Zoom slider: many Android WebViews don't report a `zoom` track capability (torch works, zoom doesn't), so the slider never showed. Add a digital-zoom fallback (always-visible slider 1-5x): CSS-scale the preview + crop the decode ROI tighter to center. Hardware zoom still used when available. - Back gesture did nothing after scanning: onDecoded closed the scan modal with the async history.go(-1) and then synchronously opened the add-product modal, so history.go unwound the NEW modal's entry and desynced the winStack/history. Add closeThen(): await the close's popstate(s) before opening the next modal.
This commit is contained in:
parent
27e7693bd3
commit
bda0cdcf21
1 changed files with 34 additions and 9 deletions
|
|
@ -1880,7 +1880,7 @@ function showChznScanModal(onSaved) {
|
|||
</div>`;
|
||||
document.body.appendChild(ov);
|
||||
|
||||
let stream = null, scanning = false, busy = false;
|
||||
let stream = null, scanning = false, busy = false, zoomFactor = 1;
|
||||
let base = winStack.length;
|
||||
let statusEl = ov.querySelector("#cz-status");
|
||||
let video = ov.querySelector("#cz-video");
|
||||
|
|
@ -1902,6 +1902,22 @@ function showChznScanModal(onSaved) {
|
|||
_skipPops = count;
|
||||
if (count > 0) history.go(-count);
|
||||
}
|
||||
// Close this modal AND wait for its history.go popstate(s) to flush before the
|
||||
// caller opens the next modal. Opening it synchronously after close() races the
|
||||
// async history.go (it would unwind the NEW modal's entry) and desyncs the back
|
||||
// stack — that was the "swipe-back does nothing" bug on scan -> add-product.
|
||||
function closeThen() {
|
||||
let count = winStack.length - base;
|
||||
for (let i = 0; i < count; i++) winStack.pop().close();
|
||||
if (count <= 0) return Promise.resolve();
|
||||
_skipPops = count;
|
||||
return new Promise(res => {
|
||||
let remaining = count;
|
||||
let h = () => { if (--remaining <= 0) { window.removeEventListener("popstate", h); res(); } };
|
||||
window.addEventListener("popstate", h);
|
||||
history.go(-count);
|
||||
});
|
||||
}
|
||||
|
||||
async function onDecoded(rawVal) {
|
||||
if (busy) return;
|
||||
|
|
@ -1910,7 +1926,7 @@ function showChznScanModal(onSaved) {
|
|||
try {
|
||||
let prefill = await ChZ.resolve(rawVal);
|
||||
stopCamera();
|
||||
close();
|
||||
await closeThen();
|
||||
showAddProductModal(onSaved, prefill);
|
||||
} catch (e) {
|
||||
busy = false;
|
||||
|
|
@ -1929,9 +1945,9 @@ function showChznScanModal(onSaved) {
|
|||
// 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 side = Math.min(vw, vh) / zoomFactor; // digital zoom crops tighter to center
|
||||
let sx = (vw - side) / 2, sy = (vh - side) / 2;
|
||||
let out = Math.min(side, 768); // enough detail, still fast
|
||||
let out = Math.min(Math.min(vw, vh), 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);
|
||||
|
|
@ -1963,16 +1979,25 @@ function showChznScanModal(onSaved) {
|
|||
track.applyConstraints({ advanced: [{ focusMode: "continuous" }] }).catch(() => {});
|
||||
}
|
||||
} catch {}
|
||||
// Zoom slider: prefer hardware zoom; otherwise fall back to digital zoom
|
||||
// (CSS-scale the preview + crop the decode ROI). Always shown so the user can
|
||||
// enlarge a small code instead of moving the phone inside its focus range.
|
||||
let zw = ov.querySelector("#cz-zoom-wrap");
|
||||
let zi = ov.querySelector("#cz-zoom");
|
||||
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.value = ((track.getSettings && track.getSettings()) || {}).zoom || caps.zoom.min;
|
||||
zi.oninput = () => { track.applyConstraints({ advanced: [{ zoom: parseFloat(zi.value) }] }).catch(() => {}); };
|
||||
zw.style.display = "flex";
|
||||
} else {
|
||||
zi.min = 1; zi.max = 5; zi.step = 0.1; zi.value = 1;
|
||||
zi.oninput = () => {
|
||||
zoomFactor = parseFloat(zi.value) || 1;
|
||||
video.style.transformOrigin = "center center";
|
||||
video.style.transform = "scale(" + zoomFactor + ")";
|
||||
};
|
||||
}
|
||||
zw.style.display = "flex";
|
||||
if (caps.torch) {
|
||||
let tb = ov.querySelector("#cz-torch");
|
||||
tb.style.display = "block";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue