优化国内构建的时间

This commit is contained in:
2025-12-07 17:16:29 +08:00
parent 9bfefd635c
commit e0fb6381d2
8 changed files with 158 additions and 7 deletions
+58 -6
View File
@@ -16,6 +16,12 @@ let videoEl = null;
let mediaStream = null;
const scannerMessage = qs('#scannerMessage');
const captureInput = qs('#captureInput');
const toggleTorchBtn = qs('#toggleTorch');
const zoomInput = qs('#zoom');
const zoomWrap = qs('#zoomWrap');
let detector = null;
let videoTrack = null;
let loopReq = null;
// 无分页模式
let currentTab = 'search';
@@ -93,8 +99,10 @@ async function openScanner(){
return;
}
if('BarcodeDetector' in window){
const formats = ['ean_13','ean_8','code_128','code_39','upc_a','upc_e'];
const detector = new window.BarcodeDetector({ formats });
const supported = await window.BarcodeDetector.getSupportedFormats();
const wanted = ['ean_13','ean_8','code_128','code_39','upc_a','upc_e'];
const useFormats = wanted.filter(f=>supported.includes(f));
detector = new window.BarcodeDetector({ formats: useFormats.length?useFormats:supported });
mediaStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } });
videoEl = document.createElement('video');
videoEl.autoplay = true;
@@ -105,9 +113,35 @@ async function openScanner(){
host.appendChild(videoEl);
await videoEl.play();
scannerMessage.classList.add('hidden');
const loop = async()=>{
try{
videoTrack = mediaStream.getVideoTracks()[0];
const caps = videoTrack.getCapabilities ? videoTrack.getCapabilities() : {};
if(caps.torch){ toggleTorchBtn.classList.remove('hidden'); }
else { toggleTorchBtn.classList.add('hidden'); }
if(caps.zoom){
zoomWrap.classList.remove('hidden');
const min = caps.zoom.min ?? 1;
const max = caps.zoom.max ?? 5;
zoomInput.min = String(min);
zoomInput.max = String(max);
zoomInput.value = String(min);
} else {
zoomWrap.classList.add('hidden');
}
}catch(_){ }
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const tick = async()=>{
try{
const codes = await detector.detect(videoEl);
const w = videoEl.videoWidth || 640;
const h = videoEl.videoHeight || 480;
const size = Math.min(w, h);
const sx = (w - size)/2;
const sy = (h - size)/2;
canvas.width = size;
canvas.height = size;
ctx.drawImage(videoEl, sx, sy, size, size, 0, 0, size, size);
const codes = await detector.detect(canvas);
if(codes && codes.length){
const text = codes[0].rawValue;
input.value = text;
@@ -117,9 +151,9 @@ async function openScanner(){
return;
}
}catch(_){ }
if(!scannerModal.classList.contains('hidden')) requestAnimationFrame(loop);
if(!scannerModal.classList.contains('hidden')){ loopReq = requestAnimationFrame(tick); }
};
requestAnimationFrame(loop);
loopReq = requestAnimationFrame(tick);
} else {
scannerMessage.textContent = '浏览器不支持实时扫码,已切换为拍照识别方式';
captureInput.click();
@@ -135,6 +169,7 @@ function closeScannerFn(){
try{ if(mediaStream){ mediaStream.getTracks().forEach(t=>t.stop()); mediaStream=null; } }catch(_){ }
try{ const host = document.getElementById('scanner'); if(host){ host.innerHTML=''; } }catch(_){ }
try{ if(scannerMessage){ scannerMessage.textContent=''; scannerMessage.classList.add('hidden'); } }catch(_){ }
try{ if(loopReq){ cancelAnimationFrame(loopReq); loopReq=null; } }catch(_){ }
scannerModal.classList.add('hidden');
}
@@ -167,6 +202,23 @@ captureInput.addEventListener('change', async ()=>{
}
});
toggleTorchBtn.addEventListener('click', async ()=>{
try{
if(!videoTrack) return;
const settings = videoTrack.getSettings ? videoTrack.getSettings() : {};
const torchOn = settings.torch === true;
await videoTrack.applyConstraints({ advanced: [{ torch: !torchOn }] });
}catch(_){ }
});
zoomInput.addEventListener('input', async ()=>{
try{
if(!videoTrack) return;
const z = Number(zoomInput.value);
await videoTrack.applyConstraints({ advanced: [{ zoom: z }] });
}catch(_){ }
});
scanBtn.addEventListener('click', openScanner);
closeScanner.addEventListener('click', closeScannerFn);
+8 -1
View File
@@ -43,7 +43,14 @@
<div id="scannerModal" class="modal hidden">
<div class="modal-content">
<div id="scannerMessage" class="scanner-message hidden"></div>
<div id="scanner"></div>
<div id="scanner" class="scanner-area"></div>
<div class="scanner-controls">
<button id="toggleTorch" class="hidden">开灯</button>
<label id="zoomWrap" class="hidden">
缩放
<input id="zoom" type="range" min="1" max="5" step="0.1" value="1" />
</label>
</div>
<button id="closeScanner">关闭</button>
</div>
</div>
+3
View File
@@ -35,6 +35,9 @@ mark{background:#ffec99}
.modal{position:fixed;inset:0;background:rgba(0,0,0,0.4);display:flex;align-items:center;justify-content:center}
.modal.hidden{display:none}
.modal-content{background:#fff;border-radius:10px;padding:12px;width:92%;max-width:480px}
.scanner-area{position:relative}
.scanner-controls{display:flex;gap:10px;align-items:center;justify-content:flex-end;margin:8px 0}
.scanner-overlay{position:absolute;inset:0;border:2px dashed rgba(25,118,210,0.6);border-radius:8px;pointer-events:none}
@media (max-width:600px){
.container{padding:12px}
.tab{padding:8px 12px}