第二次迭代优化

This commit is contained in:
andy
2026-06-18 10:58:44 +08:00
parent 3f1684e57a
commit 10fbd0390c
10 changed files with 183 additions and 18 deletions

View File

@@ -3,6 +3,7 @@ import { nextTick, onBeforeUnmount, ref } from 'vue'
import { useRouter } from 'vue-router'
import { showToast } from 'vant'
import { QrCode, X } from 'lucide-vue-next'
import jsQR from 'jsqr'
import { parseWriteOffCode, type WriteOffCodePayload } from '@/utils/writeOffCode'
const router = useRouter()
@@ -13,7 +14,10 @@ const scanError = ref('')
let stream: MediaStream | null = null
let detector: { detect: (source: HTMLVideoElement) => Promise<Array<{ rawValue: string }>> } | null = null
let canvas: HTMLCanvasElement | null = null
let canvasContext: CanvasRenderingContext2D | null = null
let frameId = 0
let lastDecodeAt = 0
const getBarcodeDetector = () => {
return (window as unknown as {
@@ -36,6 +40,8 @@ const stopScan = () => {
if (videoRef.value) {
videoRef.value.srcObject = null
}
detector = null
lastDecodeAt = 0
}
const goConfirm = (payload: WriteOffCodePayload) => {
@@ -54,14 +60,41 @@ const handleCodeValue = (rawValue: string) => {
goConfirm(payload)
}
const detectWithBarcodeDetector = async (video: HTMLVideoElement) => {
if (!detector) return ''
const results = await detector.detect(video)
return results[0]?.rawValue || ''
}
const detectWithJsQr = (video: HTMLVideoElement) => {
const width = video.videoWidth
const height = video.videoHeight
if (!width || !height) return ''
if (!canvas) {
canvas = document.createElement('canvas')
}
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width
canvas.height = height
canvasContext = canvas.getContext('2d', { willReadFrequently: true })
}
if (!canvasContext) return ''
canvasContext.drawImage(video, 0, 0, width, height)
const imageData = canvasContext.getImageData(0, 0, width, height)
return jsQR(imageData.data, width, height, { inversionAttempts: 'dontInvert' })?.data || ''
}
const detectLoop = async () => {
if (!scanning.value || !videoRef.value || !detector) return
if (!scanning.value || !videoRef.value) return
const video = videoRef.value
try {
if (video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {
const results = await detector.detect(video)
const value = results[0]?.rawValue
const now = Date.now()
if (video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA && now - lastDecodeAt > 120) {
lastDecodeAt = now
const value = detector ? await detectWithBarcodeDetector(video) : detectWithJsQr(video)
if (value) {
handleCodeValue(value)
return
@@ -79,16 +112,16 @@ const detectLoop = async () => {
const startScan = async () => {
if (scanning.value || loading.value) return
const BarcodeDetector = getBarcodeDetector()
if (!BarcodeDetector) {
scanError.value = '当前浏览器不支持摄像头扫码'
showToast('当前浏览器不支持摄像头扫码')
if (!navigator.mediaDevices?.getUserMedia) {
scanError.value = '当前浏览器无法打开摄像头'
showToast('当前浏览器无法打开摄像头')
return
}
loading.value = true
scanError.value = ''
try {
detector = new BarcodeDetector({ formats: ['qr_code'] })
detector = BarcodeDetector ? new BarcodeDetector({ formats: ['qr_code'] }) : null
stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {