⚡ qr-scanner-wechat

WASM 加速 · 微信同源引擎 · 极高识别率

说明 本 Demo 需要加载微信 WASM 扫码引擎(~2.5MB)。
WASM 文件获取方式:
1. 从 github.com/antfu/qr-scanner-wechat 获取
2. 或自行编译微信 OCR 相关 WASM 模块
3. 将 WASM 文件放置于 ./assets/wechat_qrcode.wasm
下方 Demo 包含完整集成代码,WASM 加载失败时会启用模拟模式。
点击"启动扫码"开始(将尝试加载 WASM 引擎)
识别结果:
-
* 然后全局访问 window.QrScannerWechat */ // ------------- 状态变量 ------------- let videoEl = document.getElementById('video'); let canvasEl = document.getElementById('canvas'); let ctx = null; let scanning = false; let animFrameId = null; let scanner = null; // WASM 扫描器实例 let wasmLoaded = false; let useSimulation = false; const statusEl = document.getElementById('status'); const resultBox = document.getElementById('resultBox'); const resultValue = document.getElementById('resultValue'); const btnStart = document.getElementById('btnStart'); const btnStop = document.getElementById('btnStop'); const btnSimulate = document.getElementById('btnSimulate'); const loadingBar = document.getElementById('loadingBar'); function setStatus(msg, type = '') { statusEl.textContent = msg; statusEl.className = 'status-bar ' + type; } function showResult(text) { resultValue.textContent = text; resultBox.classList.add('show'); scanning = false; btnStart.disabled = false; btnStop.disabled = true; cancelAnimationFrame(animFrameId); // 停止视频流 if (videoEl.srcObject) { videoEl.srcObject.getTracks().forEach(t => t.stop()); videoEl.srcObject = null; } } // ------------- 加载 WASM 引擎 ------------- async function loadWASMScanner() { loadingBar.classList.add('active'); setStatus('正在加载 WASM 引擎(~2.5MB),请稍候...', ''); try { /** * 实际项目中: * const { loadWASM, createScanner } = window.QrScannerWechat || require('qr-scanner-wechat'); * await loadWASM('/assets/wechat_qrcode.wasm'); * scanner = await createScanner(); */ // 本 Demo:尝试从网络加载(实际路径需自行配置) // 这里模拟加载过程 await new Promise(r => setTimeout(r, 1500)); throw new Error('WASM 文件未找到(请参考说明放置 WASM 文件)'); } catch (e) { console.warn('WASM 加载失败,将使用模拟模式:', e.message); setStatus('⚠️ WASM 未加载,已切换为模拟模式(点击"模拟识别"测试)', 'error'); useSimulation = true; loadingBar.classList.remove('active'); return false; } } // ------------- 启动摄像头 ------------- async function startCamera() { setStatus('正在请求摄像头权限...', ''); try { const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment', width: { ideal: 1280 }, height: { ideal: 720 }, }, audio: false, }); videoEl.srcObject = stream; await videoEl.play(); return true; } catch (e) { setStatus('❌ 摄像头启动失败:' + e.message, 'error'); return false; } } // ------------- 真实解码循环(WASM 模式)------------- async function decodeLoop() { if (!scanning) return; try { // 抓取当前视频帧 if (!ctx) ctx = canvasEl.getContext('2d'); canvasEl.width = videoEl.videoWidth; canvasEl.height = videoEl.videoHeight; ctx.drawImage(videoEl, 0, 0, canvasEl.width, canvasEl.height); const imageData = ctx.getImageData(0, 0, canvasEl.width, canvasEl.height); // 真实 WASM 解码调用: // const result = await scanner.decode(imageData); // if (result) { showResult(result); return; } // 模拟模式: if (useSimulation) { // 模拟模式下不自动识别,等待用户点击"模拟识别" } } catch (e) { // 忽略单帧解码错误 } animFrameId = requestAnimationFrame(decodeLoop); } // ------------- 模拟识别(WASM 不可用时的演示)------------- function simulateScan() { const mockResults = [ 'https://blog.csdn.net/weixin_39863120/article/details/148424944', 'https://github.com/antfu/qr-scanner-wechat', 'Hello, qr-scanner-wechat! 识别率极高 🚀', 'WECHAT:QRSCAN:20240601:abc123xyz', 'https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html', ]; const result = mockResults[Math.floor(Math.random() * mockResults.length)]; setStatus('✅ 模拟识别成功!(WASM 模式将返回真实结果)', 'success'); showResult(result); } // ------------- 按钮事件 ------------- btnStart.addEventListener('click', async () => { btnStart.disabled = true; // 加载 WASM(首次) if (!wasmLoaded && !useSimulation) { const ok = await loadWASMScanner(); if (ok) wasmLoaded = true; } // 启动摄像头 const cameraOk = await startCamera(); if (!cameraOk) { btnStart.disabled = false; return; } scanning = true; btnStop.disabled = false; setStatus('扫描中...(WASM 模式:每帧实时解码)', ''); // 启动解码循环 decodeLoop(); }); btnStop.addEventListener('click', () => { scanning = false; cancelAnimationFrame(animFrameId); if (videoEl.srcObject) { videoEl.srcObject.getTracks().forEach(t => t.stop()); videoEl.srcObject = null; } btnStart.disabled = false; btnStop.disabled = true; setStatus('已停止', ''); }); btnSimulate.addEventListener('click', () => { if (!scanning) { setStatus('⚠️ 请先点击"启动扫码"', 'error'); return; } simulateScan(); });