feat: 优化二维码生成逻辑
This commit is contained in:
1 parent
0e4ccae5d3
commit
0ba17ace6f
2 files changed
+34
-44
No files matched your search
@@ -9,52 +9,45 @@ import QRCode from 'qrcode';
|
|||||||
import { onMounted, ref, watchEffect } from 'vue';
|
import { onMounted, ref, watchEffect } from 'vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
value: string
|
value: string | Uint8Array,
|
||||||
|
width?: number
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const width = ref(600)
|
const width = ref(props.width ?? 600)
|
||||||
|
|
||||||
const canvasRefRGB = ref<HTMLCanvasElement>()
|
const canvasRefRGB = ref<HTMLCanvasElement>()
|
||||||
|
|
||||||
const generateQRCodeCanvasData = (data: string) => {
|
const generateQRCodeCanvasData = (data: Uint8Array) => {
|
||||||
const canvas = document.createElement('canvas')
|
const canvas = document.createElement('canvas')
|
||||||
canvas.width = width.value
|
canvas.width = width.value
|
||||||
canvas.height = width.value
|
canvas.height = width.value
|
||||||
const ctx = canvas.getContext('2d')!
|
const ctx = canvas.getContext('2d')!
|
||||||
QRCode.toCanvas(canvas, [{ data: new TextEncoder().encode(data), mode: 'byte' }], {
|
QRCode.toCanvas(canvas, [{ data, mode: 'byte' }], {
|
||||||
width: width.value,
|
width: width.value,
|
||||||
})
|
})
|
||||||
const imageData = ctx.getImageData(0, 0, width.value, width.value)
|
const imageData = ctx.getImageData(0, 0, width.value, width.value)
|
||||||
return imageData
|
return imageData
|
||||||
}
|
}
|
||||||
|
|
||||||
const paddingData = (data: string) => {
|
const paddingData = (data: Uint8Array) => {
|
||||||
const b64Data = btoa(data)
|
if(data.length % 3 === 0) {
|
||||||
if (b64Data.length % 3 === 0) {
|
return new Uint8Array([...data, 0, 1, 2])
|
||||||
return b64Data + '012'
|
} else if(data.length % 3 === 1) {
|
||||||
} else if (b64Data.length % 3 === 1) {
|
return new Uint8Array([...data, 0, 1])
|
||||||
return b64Data + '01'
|
|
||||||
} else {
|
} else {
|
||||||
return b64Data + '0'
|
return new Uint8Array([...data, 0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
const padedData = paddingData(props.value)
|
const u8iData = props.value instanceof Uint8Array ? props.value : new TextEncoder().encode(props.value)
|
||||||
|
const padedData = paddingData(u8iData)
|
||||||
const data = {
|
const data = {
|
||||||
r: padedData.slice(0, padedData.length / 3),
|
r: padedData.slice(0, padedData.length / 3),
|
||||||
g: padedData.slice(padedData.length / 3, padedData.length / 3 * 2),
|
g: padedData.slice(padedData.length / 3, padedData.length / 3 * 2),
|
||||||
b: padedData.slice(padedData.length / 3 * 2, padedData.length)
|
b: padedData.slice(padedData.length / 3 * 2, padedData.length)
|
||||||
}
|
}
|
||||||
// console.log({
|
|
||||||
// data,
|
|
||||||
// ECData: {
|
|
||||||
// r: new TextEncoder().encode(data.r),
|
|
||||||
// g: new TextEncoder().encode(data.g),
|
|
||||||
// b: new TextEncoder().encode(data.b)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
const imgData = {
|
const imgData = {
|
||||||
r: generateQRCodeCanvasData(data.r),
|
r: generateQRCodeCanvasData(data.r),
|
||||||
g: generateQRCodeCanvasData(data.g),
|
g: generateQRCodeCanvasData(data.g),
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="qr-scanner-wrapper">
|
<div class="qr-scanner-wrapper">
|
||||||
<video ref="scannerDisplayRef"></video>
|
<video ref="scannerDisplayRef"></video>
|
||||||
|
<div>res: {{ res }}</div>
|
||||||
<canvas ref="testCtx" style="transform: scale(.6);" v-show="true"></canvas>
|
<canvas ref="testCtx" style="transform: scale(.6);" v-show="true"></canvas>
|
||||||
<canvas ref="testCtxR" style="transform: scale(.6);" v-show="true"></canvas>
|
<canvas ref="testCtxR" style="transform: scale(.6);" v-show="true"></canvas>
|
||||||
<canvas ref="testCtxG" style="transform: scale(.6);" v-show="true"></canvas>
|
<canvas ref="testCtxG" style="transform: scale(.6);" v-show="true"></canvas>
|
||||||
<canvas ref="testCtxB" style="transform: scale(.6);" v-show="true"></canvas>
|
<canvas ref="testCtxB" style="transform: scale(.6);" v-show="true"></canvas>
|
||||||
<div>{{ res }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ import jsQR from 'jsqr'
|
|||||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
onResult: [value: string]
|
onResult: [value: Uint8Array]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const scannerDisplayRef = ref<HTMLVideoElement>()
|
const scannerDisplayRef = ref<HTMLVideoElement>()
|
||||||
@@ -22,27 +22,26 @@ const testCtx = ref<HTMLCanvasElement>()
|
|||||||
const testCtxR = ref<HTMLCanvasElement>()
|
const testCtxR = ref<HTMLCanvasElement>()
|
||||||
const testCtxG = ref<HTMLCanvasElement>()
|
const testCtxG = ref<HTMLCanvasElement>()
|
||||||
const testCtxB = ref<HTMLCanvasElement>()
|
const testCtxB = ref<HTMLCanvasElement>()
|
||||||
// const result = reactive<string[]>([])
|
|
||||||
const res = ref<string>('')
|
const res = ref<string>('')
|
||||||
|
|
||||||
let stream: MediaStream | null = null
|
let stream: MediaStream | null = null
|
||||||
let timer: any | undefined = undefined
|
let timer: any | undefined = undefined
|
||||||
|
|
||||||
const unpaddingData = (data: string) => {
|
const u8iEndWith = (data: Uint8Array, target: number[]) => {
|
||||||
console.log(data)
|
for (let i = 0; i < target.length; i++) {
|
||||||
let res: string
|
if (data[data.length - 1 - i] !== target[target.length - 1 - i]) return false
|
||||||
try {
|
}
|
||||||
if (data.endsWith('012')) {
|
return true
|
||||||
res = atob(data.slice(0, data.length - 3))
|
}
|
||||||
} else if (data.endsWith('01')) {
|
|
||||||
res = atob(data.slice(0, data.length - 2))
|
const unpaddingData = (data: Uint8Array): Uint8Array => {
|
||||||
|
if(u8iEndWith(data, [0, 1, 2])) {
|
||||||
|
return data.slice(0, data.length - 3)
|
||||||
|
} else if (u8iEndWith(data, [0, 1])) {
|
||||||
|
return data.slice(0, data.length - 2)
|
||||||
} else {
|
} else {
|
||||||
res = atob(data.slice(0, data.length - 1))
|
return data.slice(0, data.length - 1)
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const binarization = (
|
const binarization = (
|
||||||
@@ -59,7 +58,7 @@ const binarization = (
|
|||||||
}
|
}
|
||||||
// const scannerResult = new Set()
|
// const scannerResult = new Set()
|
||||||
|
|
||||||
const getQRDataFromVideoFrame = (videoFrame: VideoFrame): string | undefined => {
|
const getQRDataFromVideoFrame = (videoFrame: VideoFrame): Uint8Array | undefined => {
|
||||||
const canvas = testCtx.value!
|
const canvas = testCtx.value!
|
||||||
canvas.width = videoFrame.codedWidth
|
canvas.width = videoFrame.codedWidth
|
||||||
canvas.height = videoFrame.codedHeight
|
canvas.height = videoFrame.codedHeight
|
||||||
@@ -77,8 +76,8 @@ const getQRDataFromVideoFrame = (videoFrame: VideoFrame): string | undefined =>
|
|||||||
for (let i = 0; i < imageData.data.length; i += 4) {
|
for (let i = 0; i < imageData.data.length; i += 4) {
|
||||||
const rgba = {
|
const rgba = {
|
||||||
r: binarization(imageData.data[i]),
|
r: binarization(imageData.data[i]),
|
||||||
g: binarization(imageData.data[i+1]),
|
g: binarization(imageData.data[i + 1]),
|
||||||
b: binarization(imageData.data[i+2]),
|
b: binarization(imageData.data[i + 2]),
|
||||||
a: imageData.data[i + 3]
|
a: imageData.data[i + 3]
|
||||||
}
|
}
|
||||||
imageDataRGB.r.data[i] = rgba.r
|
imageDataRGB.r.data[i] = rgba.r
|
||||||
@@ -107,11 +106,9 @@ const getQRDataFromVideoFrame = (videoFrame: VideoFrame): string | undefined =>
|
|||||||
const codeR = jsQR(imageDataRGB.r.data, imageDataRGB.r.width, imageDataRGB.r.height)
|
const codeR = jsQR(imageDataRGB.r.data, imageDataRGB.r.width, imageDataRGB.r.height)
|
||||||
const codeG = jsQR(imageDataRGB.g.data, imageDataRGB.g.width, imageDataRGB.g.height)
|
const codeG = jsQR(imageDataRGB.g.data, imageDataRGB.g.width, imageDataRGB.g.height)
|
||||||
const codeB = jsQR(imageDataRGB.b.data, imageDataRGB.b.width, imageDataRGB.b.height)
|
const codeB = jsQR(imageDataRGB.b.data, imageDataRGB.b.width, imageDataRGB.b.height)
|
||||||
// console.log({
|
|
||||||
// codeR, codeG, codeB
|
|
||||||
// })
|
|
||||||
if (!codeR || !codeG || !codeB) return undefined
|
if (!codeR || !codeG || !codeB) return undefined
|
||||||
const data = unpaddingData(codeR?.data + codeG?.data + codeB?.data)
|
const resBuffer = new Uint8Array([...codeR.binaryData, ...codeG.binaryData, ...codeB.binaryData])
|
||||||
|
const data = unpaddingData(resBuffer)
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +123,7 @@ const handleTrackProcessor = async (track: MediaStreamTrack) => {
|
|||||||
const frameFromCamera = result.value as VideoFrame;
|
const frameFromCamera = result.value as VideoFrame;
|
||||||
const data = getQRDataFromVideoFrame(frameFromCamera)
|
const data = getQRDataFromVideoFrame(frameFromCamera)
|
||||||
if (data) {
|
if (data) {
|
||||||
res.value = data
|
res.value = new TextDecoder().decode(data)
|
||||||
console.log(data)
|
console.log(data)
|
||||||
emit('onResult', data)
|
emit('onResult', data)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user