Просмотр исходного кода

fix(uniapp-scan): 弃用 BarcodeDetector 直接用 jsQR 走主方案

原代码优先 BarcodeDetector,iOS Safari 16.4 早期版本虽然有这 API 但
识别率很差(很多 QR 扫不出来,反而卡住流程),iOS Safari 17 之前版本
体验更差。

修法:直接用 jsQR 走主方案
- jsQR 纯 JS,iOS Safari 11+ 全支持,识别率稳定
- BarcodeDetector 完全移除,代码更简单
- 用户体感:扫码即能扫到,不再卡顿

影响:inspection/scan + qualityInspection/scan
yangjingjing 1 день назад
Родитель
Сommit
1089cc486c
2 измененных файлов с 37 добавлено и 83 удалено
  1. 21 43
      src/pages/inspection/scan/index.vue
  2. 16 40
      src/pages/qualityInspection/scan/index.vue

+ 21 - 43
src/pages/inspection/scan/index.vue

@@ -82,8 +82,10 @@ function startScan() {
 
 // ============================== H5 端扫码实现 ==============================
 async function h5StartBarcodeScanner() {
+  console.log('[H5 扫码] start')
   // 1. 检测摄像头权限 API
   if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
+    console.warn('[H5 扫码] 浏览器不支持 getUserMedia')
     tipText.value = '浏览器不支持获取摄像头,请点击下方"手动输入"'
     return
   }
@@ -93,6 +95,7 @@ async function h5StartBarcodeScanner() {
       video: { facingMode: { ideal: 'environment' } },
       audio: false
     })
+    console.log('[H5 扫码] getUserMedia 成功')
   } catch (e: any) {
     if (e?.name === 'NotAllowedError' || e?.name === 'PermissionDeniedError') {
       tipText.value = '摄像头权限被拒绝,请在浏览器设置中允许后重试'
@@ -124,43 +127,13 @@ async function h5StartBarcodeScanner() {
     console.warn('[H5 扫码] video.play 失败', e)
   }
 
-  // 4. 选 QR 识别方案:
-  //    优先 BarcodeDetector(Chrome/Edge/iOS 17+ 性能好),
-  //    不可用时 fallback jsQR(纯 JS,兼容老浏览器但稍慢)
-  const BarcodeDetectorCtor: any = (window as any).BarcodeDetector
-  if (typeof BarcodeDetectorCtor === 'function') {
-    let detector: any
-    try {
-      detector = new BarcodeDetectorCtor({ formats: ['qr_code'] })
-    } catch (e) {
-      // ignore
-    }
-    if (detector) {
-      const tick = async () => {
-        if (!h5Stream) return
-        try {
-          const codes = await detector.detect(video)
-          if (codes && codes.length && codes[0].rawValue) {
-            h5StopScanner()
-            scanResult.value = codes[0].rawValue
-            onScanSuccess(codes[0].rawValue)
-            return
-          }
-        } catch (e) {
-          // 单帧失败忽略
-        }
-        h5RafId = requestAnimationFrame(tick)
-      }
-      h5RafId = requestAnimationFrame(tick)
-      return
-    }
-  }
-
-  // 5. Fallback:jsQR(用 canvas 抽帧识别)
-  tipText.value = '正在用 jsQR 识别中…'
+  // 4. 选 QR 识别方案:直接用 jsQR(纯 JS,兼容性最好,iOS Safari 11+ 全支持)
+  //    BarcodeDetector 在 iOS Safari 16.4 早期版本有 bug,直接弃用
+  tipText.value = '正在识别中…'
   const canvas = document.createElement('canvas')
   const ctx = canvas.getContext('2d', { willReadFrequently: true })
   if (!ctx) {
+    console.warn('[H5 扫码] canvas 2d 不可用')
     tipText.value = '当前浏览器不支持 QR 码识别,请点击下方"手动输入"'
     return
   }
@@ -170,15 +143,20 @@ async function h5StartBarcodeScanner() {
       canvas.width = video.videoWidth
       canvas.height = video.videoHeight
       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, {
-        inversionAttempts: 'dontInvert'
-      })
-      if (code && code.data) {
-        h5StopScanner()
-        scanResult.value = code.data
-        onScanSuccess(code.data)
-        return
+      try {
+        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
+        const code = jsQR(imageData.data, imageData.width, imageData.height, {
+          inversionAttempts: 'dontInvert'
+        })
+        if (code && code.data) {
+          console.log('[H5 扫码] 命中:', code.data)
+          h5StopScanner()
+          scanResult.value = code.data
+          onScanSuccess(code.data)
+          return
+        }
+      } catch (e) {
+        console.warn('[H5 扫码] 单帧识别失败', e)
       }
     }
     h5RafId = requestAnimationFrame(tick)

+ 16 - 40
src/pages/qualityInspection/scan/index.vue

@@ -121,37 +121,9 @@ async function h5StartBarcodeScanner() {
   } catch (e) {
     console.warn('[H5 扫码] video.play 失败', e)
   }
-  // 优先 BarcodeDetector,不可用则 jsQR fallback
-  const BarcodeDetectorCtor: any = (window as any).BarcodeDetector
-  if (typeof BarcodeDetectorCtor === 'function') {
-    let detector: any
-    try {
-      detector = new BarcodeDetectorCtor({ formats: ['qr_code'] })
-    } catch (e) {
-      // ignore
-    }
-    if (detector) {
-      const tick = async () => {
-        if (!h5Stream) return
-        try {
-          const codes = await detector.detect(video)
-          if (codes && codes.length && codes[0].rawValue) {
-            h5StopScanner()
-            scanResult.value = codes[0].rawValue
-            onScanSuccess(codes[0].rawValue)
-            return
-          }
-        } catch (e) {
-          // ignore
-        }
-        h5RafId = requestAnimationFrame(tick)
-      }
-      h5RafId = requestAnimationFrame(tick)
-      return
-    }
-  }
-  // jsQR fallback
-  tipText.value = '正在用 jsQR 识别中…'
+  // 直接走 jsQR(纯 JS,iOS Safari 11+ 全支持)
+  // 弃用 BarcodeDetector:iOS Safari 16.4 早期版本有 bug,实际识别率差
+  tipText.value = '正在识别中…'
   const canvas = document.createElement('canvas')
   const ctx = canvas.getContext('2d', { willReadFrequently: true })
   if (!ctx) {
@@ -164,15 +136,19 @@ async function h5StartBarcodeScanner() {
       canvas.width = video.videoWidth
       canvas.height = video.videoHeight
       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, {
-        inversionAttempts: 'dontInvert'
-      })
-      if (code && code.data) {
-        h5StopScanner()
-        scanResult.value = code.data
-        onScanSuccess(code.data)
-        return
+      try {
+        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
+        const code = jsQR(imageData.data, imageData.width, imageData.height, {
+          inversionAttempts: 'dontInvert'
+        })
+        if (code && code.data) {
+          h5StopScanner()
+          scanResult.value = code.data
+          onScanSuccess(code.data)
+          return
+        }
+      } catch (e) {
+        // ignore
       }
     }
     h5RafId = requestAnimationFrame(tick)