🔍 Add support for barcode scanning and improve QR code parsing logic

This commit is contained in:
2026-04-15 00:24:49 +08:00
parent d6798e31de
commit 447d65a28c
2 changed files with 92 additions and 62 deletions

1
parse-qr/html5-qrcode.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -7,6 +7,7 @@
<link rel="icon" href="https://cdn.hatter.ink/doc/18811_3C6AA21718A09D034E40D557D51E7787/qr.png" type="image/png">
<link rel="shortcut icon" href="https://cdn.hatter.ink/doc/18811_3C6AA21718A09D034E40D557D51E7787/qr.png" type="image/png">
<link rel="apple-touch-icon" href="https://cdn.hatter.ink/doc/18811_3C6AA21718A09D034E40D557D51E7787/qr.png">
<script src="html5-qrcode.min.js"></script>
<script src="jsQR.min.js"></script>
<style>
* {
@@ -50,6 +51,7 @@
color: #333;
margin-bottom: 10px;
position: relative;
font-size: 20px;
}
.refresh-btn {
@@ -380,7 +382,7 @@
<body>
<div class="container">
<h1>
🔍 QR Code Parser
🔍 QR & Barcode Parser
<button class="refresh-btn" id="refreshBtn" title="Refresh">🔄</button>
</h1>
<p class="subtitle">Drag & drop, paste, or click to browse an image</p>
@@ -415,10 +417,12 @@
<div class="camera-modal" id="cameraModal">
<div class="camera-status" id="cameraStatus">Starting camera...</div>
<video id="video" autoplay playsinline></video>
<div id="qr-reader" style="width: 100%;"></div>
<button class="close-camera" id="closeCamera">Close</button>
</div>
<div style="display: none;"><div id="qr-reader-hidden"></div></div>
<script>
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('fileInput');
@@ -437,8 +441,7 @@
const historyList = document.getElementById('historyList');
const clearHistoryBtn = document.getElementById('clearHistoryBtn');
const HISTORY_KEY = 'qr_scan_history';
let stream = null;
let scanInterval = null;
let html5QrcodeScanner = null;
function loadHistory() {
const stored = localStorage.getItem(HISTORY_KEY);
@@ -587,36 +590,61 @@
preview.classList.add('visible');
dropZone.classList.add('has-image');
decodeQR(e.target.result);
decodeQR(file);
};
reader.readAsDataURL(file);
}
function decodeQR(dataURL) {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
function decodeQR(file) {
// 尝试使用 html5-qrcode (支持一维码和二维码)
if (typeof Html5Qrcode !== 'undefined') {
const html5QrCode = new Html5Qrcode('qr-reader-hidden');
html5QrCode.scanFile(file, true)
.then(decodedText => {
hideLoading();
if (decodedText) {
showResult(decodedText);
} else {
showError('No QR code or barcode found in the image. Please try with a clearer image.');
}
})
.catch(err => {
hideLoading();
console.error('Scan error:', err);
showError('Failed to decode. Please try with a clearer image.');
});
} else {
// 回退到 jsQR (仅支持二维码)
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const code = jsQR(imageData.data, imageData.width, imageData.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const code = jsQR(imageData.data, imageData.width, imageData.height);
hideLoading();
hideLoading();
if (code) {
showResult(code.data);
} else {
showError('No QR code found in the image. Please try with a clearer image.');
}
};
img.onerror = () => {
hideLoading();
showError('Failed to load image');
};
img.src = dataURL;
if (code) {
showResult(code.data);
} else {
showError('No QR code found in the image. Please try with a clearer image.');
}
};
img.onerror = () => {
hideLoading();
showError('Failed to load image');
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
}
}
function showResult(text) {
@@ -663,53 +691,54 @@
location.reload();
});
cameraBtn.addEventListener('click', async () => {
try {
stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'environment' }
});
video.srcObject = stream;
cameraModal.classList.add('visible');
cameraStatus.textContent = 'Scanning...';
startScan();
} catch (err) {
cameraStatus.textContent = 'Camera access denied or not available';
console.error('Camera error:', err);
}
cameraBtn.addEventListener('click', () => {
cameraModal.classList.add('visible');
cameraStatus.textContent = 'Starting camera...';
startScan();
});
closeCamera.addEventListener('click', stopCamera);
function stopCamera() {
if (stream) {
stream.getTracks().forEach(track => track.stop());
stream = null;
if (html5QrcodeScanner) {
html5QrcodeScanner.stop().then(() => {
html5QrcodeScanner.clear();
html5QrcodeScanner = null;
}).catch(err => {
console.error('Failed to stop scanner:', err);
});
}
if (scanInterval) {
clearInterval(scanInterval);
scanInterval = null;
}
video.srcObject = null;
cameraModal.classList.remove('visible');
cameraStatus.textContent = 'Starting camera...';
}
function startScan() {
scanInterval = setInterval(() => {
if (video.readyState === video.HAVE_ENOUGH_DATA) {
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const code = jsQR(imageData.data, imageData.width, imageData.height);
if (code) {
showResult(code.data);
if (typeof Html5Qrcode !== 'undefined') {
html5QrcodeScanner = new Html5Qrcode('qr-reader');
html5QrcodeScanner.start(
{ facingMode: 'environment' },
{
fps: 10,
qrbox: { width: 250, height: 250 }
},
(decodedText) => {
showResult(decodedText);
stopCamera();
},
(errorMessage) => {
// Scanning, no code found yet
}
}
}, 100);
).then(() => {
cameraStatus.textContent = 'Scanning...';
}).catch(err => {
cameraStatus.textContent = 'Camera access denied or not available';
console.error('Camera error:', err);
});
} else {
// 回退到 jsQR
cameraStatus.textContent = 'Camera not available, using fallback...';
}
}
</script>
</body>