H5实现手机扫描二维码识别

430 阅读1分钟

主要依赖于二维码解析库jsQR,它是一个纯javascript的二维码阅读库。 这个库接收原始图像,并将定位、提取和解析其中发现的任何QR码。

jsQR 被设计成一个完全独立的库,用于扫描二维码。按照设计,它不包含任何特定于平台的代码。这使它可以轻松扫描前端网络摄像头流、用户上传的图像,或用作后端 Node.js 进程的一部分。

主要用法

npm install jsqr --save

// ES6 import
import jsQR from "jsqr";

const code = jsQR(imageData, width, height, options?);
if (code) {
  console.log("Found QR code", code);
}

其中,imageData数据格式与表示元素区域的底层像素数据的ImageData接口相同。

大致原理步骤:

  1. h5摄像头mediaDevices获取stream流
  2. video.src = 1
  3. canvas.drawImage(2)
  4. ctx.getImageData(3)
  5. jsQR(4)

具体代码

<script setup>
import {
    ref,
    nextTick,
    onMounted,
    onBeforeUnmount
} from 'vue';
import jsQR from 'jsqr';
import './index.scss';

let video = ref(null);
let canvas = ref(null);
let ctx = null;
let videoWH = {
    width: document.documentElement.clientWidth,
    height: document.documentElement.clientHeight
};

onMounted(async () => {
    await nextTick();
    init();
});
onBeforeUnmount(() => {
    fullStop();
});
const tick = () => {
    if (
        video.value &&
        video.value.readyState === video.value.HAVE_ENOUGH_DATA
    ) {
        canvas.value.height = videoWH.height;
        canvas.value.width = videoWH.width;
        ctx.drawImage(
            video.value,
            0,
            0,
            canvas.value.width,
            canvas.value.height
        );
        const imageData = ctx.getImageData(
            0,
            0,
            canvas.value.width,
            canvas.value.height
        );
        let code = false;
        try {
            code = jsQR(imageData.data, imageData.width, imageData.height);
        } catch (e) {
            console.error(e);
        }
        if (code) {
            found(code.data);
        }
    }
    run();
};
// 初始化
const init = () => {
    ctx = canvas.value.getContext('2d');
    const facingMode = { exact: 'environment', video: true };
    const handleSuccess = (stream) => {
        video.value.src = stream;
        video.value.playsInline = true;
        const playPromise = video.value.play();
        playPromise.then(run);
    };
    navigator.mediaDevices.getUserMedia({ video: { facingMode } })
        .then(handleSuccess)
        .catch(() => { 
            errorCaptured(error); 
        });
    }
};
const run = () => {
    requestAnimationFrame(tick);
};
const found = (code) => {
    codeScanned(code);
    stop()
};
// 完全停止
const stop = () => {
    if (video.value && video.value.srcObject) {
        video.value.srcObject.getTracks().forEach((t) => t.stop());
    }
};
const codeScanned = (code) => {
    setTimeout(() => {
        alert(`扫码解析成功: ${code}`);
    }, 200);
};
const errorCaptured = (error) => {
    console.error(error);
};
</script>