文章介绍
这里使用了jsQR和zxing两种方式,分别在普通的H5和vue中使用,文章附上完整demo和一些注意事项
注意事项
这里H5也好,vue也好,如果想要部署到服务器上,需要用https协议,否则无法使用。本地启动项目的时候,很多人在vue中无法实现该效果,那是因为需要我们从localhost路径打开,否则也无法使用摄像头
vue 或 H5,使用jsQR
使用jsQR这个JS库
这里附上这个库的地址:https://s3.gendome.net/activity/js/jsQR.js
先把上面这个JS文件下载下来,比如我这里命名为jsQR.js,使用我这样的写法
H5中
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./jsQR.js"></script> </head> <body> <video style="display: none;" id="video"></video> <canvas style="width: 100%; height: 100%;" id="canvas"></canvas> <canvas style="display: none;" id="2d"></canvas> </body> <script type="text/javascript"> var video = document.createElement("video"); var canvasElement = document.getElementById("canvas"); var canvas = canvasElement.getContext("2d"); // 尝试打开手机上安装后置摄像头 navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then(function (stream) { video.srcObject = stream; // 阻止IOS视频全屏 video.setAttribute("playsinline", true); video.play(); requestAnimationFrame(tick); }); function tick() { if (video.readyState === video.HAVE_ENOUGH_DATA) { canvasElement.hidden = false; canvasElement.height = video.videoHeight; canvasElement.width = video.videoWidth; canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height); var imageData = canvas.getImageData(0, 0, canvasElement.width, canvasElement.height); // QR码解析 var code = jsQR( imageData.data, // 图像数据 imageData.width, // 宽度 imageData.height, // 高度 { inversionAttempts: "dontInvert", } ); if (code) { console.log(code.data); } } requestAnimationFrame(tick); } </script> </html>
复制
vue中
<template> <div> <video style="display: none" id="video"></video> <canvas style="width: 100vw; margin-top: 13vw" id="canvas"></canvas> <canvas style="display: none" id="2d"></canvas> </div> </template> <script setup> import { ref, onMounted} from "vue"; import "@/utils/jsQR.js"; // 添加关闭摄像头的函数 const stopMediaTracks = () => { if (streams) { streams.getTracks().forEach((track) => track.stop()); } video.srcObject = null; }; const streams = ref(null); // 初始化 stream 变量 onMounted(() => { var video = document.createElement("video"); var canvasElement = document.getElementById("canvas"); var canvas = canvasElement.getContext("2d"); console.log(navigator.mediaDevices); // 尝试打开手机上安装后置摄像头 navigator.mediaDevices .getUserMedia({ video: { facingMode: "environment" }, }) .then(function (stream) { streams.value = stream; video.srcObject = stream; // 阻止IOS视频全屏 video.setAttribute("playsinline", true); video.play(); requestAnimationFrame(tick); }); function tick() { if (video.readyState === video.HAVE_ENOUGH_DATA) { canvasElement.hidden = false; canvasElement.height = video.videoHeight; canvasElement.width = video.videoWidth; canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height); var imageData = canvas.getImageData( 0, 0, canvasElement.width, canvasElement.height ); // QR码解析 var code = jsQR( imageData.data, // 图像数据 imageData.width, // 宽度 imageData.height, // 高度 { inversionAttempts: "dontInvert", } ); if (code) { console.log(code.data); alert(code.data); } } requestAnimationFrame(tick); } }); </script>
复制
优点:
jsQR是一个完全独立的javascript脚本库,可以用于扫描QR码。
它不限制于任何平添,可以轻松地扫描前端网络摄像头流、用户上传的图像。
如果使用jsQR扫描网络摄像头流,则需要从视频流中提取图像数据。接着可以将其传递给jsQR。
VUE中,使用zxing库
zxing
是一款由用 Typescript 编写的一维/二维条码图像处理库,即条形码与二维码,它是由 Java 版本 ZXing 库移植而来的。
git地址:https://github.com/zxing-js/library
在线案例:ZXing TypeScript | Demo & Examples
首先安装:
npm install @zxing/library -S
复制
vue文件中:
<template> <button @click="scanner">扫码</button> <button @click="close">关闭</button> <div class="container"> <video id="video"></video> <div v-if="mask" class="mask"></div> </div> <h4>识别到的信息:<span style="color: red;">{{ message }}</span></h4> </template> <script setup> import { onMounted, ref } from "vue"; import { BrowserMultiFormatReader } from '@zxing/library'; let message = ref(''); let codeReader = null; let selectedDeviceId = ''; let mask = ref(false); function init() { // 识别和处理多种常见的条形码和二维码格式 codeReader = new BrowserMultiFormatReader(); // 获取当前设备上可用的视频输入设备列表 codeReader.getVideoInputDevices().then(videoInputDevices => { if (videoInputDevices.length > 1) { // 后缀摄像头(手机) selectedDeviceId = videoInputDevices[1].deviceId; }else { // 前置摄像头 selectedDeviceId = videoInputDevices[0].deviceId; } }) } onMounted(() => { init(); }) function scanner() { mask.value = true // 自动打开指定的视频输入设备,并实时对视频流中的每一帧图像进行条形码和二维码的解码操作,此方法不是只进行一次解码尝试,而是在视频流持续传输的过程中,不断地对每一帧图像进行解码分析 codeReader.decodeFromVideoDevice(selectedDeviceId, 'video', (result, err) => { if (result) { message.value = result.text; } if (err) {} }) } function close() { codeReader.reset(); message.value = ''; mask.value = false; } </script> <style> .container { width: 100%; height: 60vh; position: relative; margin-top: 10px; overflow: hidden; } .container > video { width: 100%; height: 100%; object-fit: cover; } .mask { position: absolute; left: 15%; top: 20%; max-width: 100%; width: 70%; height: 60%; border-radius: 2px; outline: rgba(0, 0, 0, .25) solid 20vmax; } </style>
复制
收尾
在调用摄像头的时候,当前页面或者组件销毁时,一定要记得关闭摄像头,否则在某些设备上,摄像头会一直保持开启状态,如果后面我发现了更好用,更方便的组件,我也会继续补充到当前文章中