实现二维码或条形码的扫描识别比较普遍的做法是去调用微信 JS-SDK 的扫一扫功能(详见 概述 | 微信开放文档),或者支付宝 H5 开放的API(详见 支付宝H5开放文档)。
但是这两者都会比较麻烦且有一定的局限性,微信的扫一扫只能在微信里用,而且还需要公众号认证等配置操作。支付宝在内置 App 内可以同时识别二维码和条形码,但外部调用的 API 无法一次性同时识别,只能分开识别。
我这里就提供一个直接使用的开源库:https://github.com/zxing-js/library,本人移动端前端开发的框架是 Vue,组件库用的是 Vant,本文方案只要开发时用的电脑具有摄像头就可以实现效果预览。
这里分享两个在线工具
1、免费在线条形码生成器-条码生成制作工具
2、草料二维码生成器 或者 点击这里
可以看到这样操作不用经过任何打包(有的需要打包成 app 才行)、部署(有的需要部署到 https 的服务器才行)、配置(前面说的诸如微信开发的配置等...)。
1、安装。
1 |
npm install @zxing/library --save |
2、假设场景:页面上有个按钮,点击触发扫码功能 @click='scanCode()',在 methods 写入该方法。
1 2 3 4 5 6 |
scanCode() { console.log('浏览器信息', navigator.userAgent); this.$router.push({ path: '/scanCodePage' }); } |
同时在 vue-router 写入对应页面的路由。
1 2 3 4 5 6 |
{ title: '扫码页面', name: 'scanCodePage', path: '/scanCodePage', component: () => import('@/views/scanCodePage.vue') } |
3、扫码页面代码,通过与 video 标签结合使用,把以下代码直接全部拷贝到新建的一个 scanCodePage.vue 文件里使用,读者在注释的地方自行根据需求,编写后续的业务代码即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
<template> <div class="page-scan"> <!--返回--> <van-nav-bar title="扫描二维码/条形码" fixed @click-left="clickIndexLeft()" class="scan-index-bar"> <template #left> <van-icon name="arrow-left" size="18" color="#fff"/> <span style="color: #fff"> 取消 </span> </template> </van-nav-bar> <!-- 扫码区域 --> <video ref="video" id="video" class="scan-video" autoplay></video> <!-- 提示语 --> <div v-show="tipShow" class="scan-tip"> {{tipMsg}} </div> </div> </template>
<script> import { BrowserMultiFormatReader } from '@zxing/library'; import { Dialog, Notify } from 'vant';
export default { name: 'scanCodePage', data() { return { loadingShow: false, codeReader: null, scanText: '', vin: null, tipMsg: '正在尝试识别....', tipShow: false } }, created() { this.codeReader = new BrowserMultiFormatReader(); this.openScan(); }, destroyed(){ this.codeReader.reset(); }, watch: { '$route'(to, from) { if(to.path == '/scanCodePage'){ this.codeReader = new BrowserMultiFormatReader(); this.openScanTwo(); } } }, methods: { async openScan() { this.codeReader.getVideoInputDevices().then((videoInputDevices) => { this.tipShow = true; this.tipMsg = '正在调用摄像头...'; console.log('videoInputDevices', videoInputDevices); // 默认获取第一个摄像头设备id let firstDeviceId = videoInputDevices[0].deviceId; // 获取第一个摄像头设备的名称 const videoInputDeviceslablestr = JSON.stringify(videoInputDevices[0].label); if (videoInputDevices.length > 1) { // 判断是否后置摄像头 if (videoInputDeviceslablestr.indexOf('back') > -1) { firstDeviceId = videoInputDevices[0].deviceId; } else { firstDeviceId = videoInputDevices[1].deviceId; } } this.decodeFromInputVideoFunc(firstDeviceId); }).catch(err => { this.tipShow = false; console.error(err); }); }, async openScanTwo() { this.codeReader = await new BrowserMultiFormatReader(); this.codeReader.getVideoInputDevices().then((videoInputDevices) => { this.tipShow = true; this.tipMsg = '正在调用摄像头...'; console.log('videoInputDevices', videoInputDevices); // 默认获取第一个摄像头设备id let firstDeviceId = videoInputDevices[0].deviceId; // 获取第一个摄像头设备的名称 const videoInputDeviceslablestr = JSON.stringify(videoInputDevices[0].label); if (videoInputDevices.length > 1) { // 判断是否后置摄像头 if (videoInputDeviceslablestr.indexOf('back') > -1) { firstDeviceId = videoInputDevices[0].deviceId; } else { firstDeviceId = videoInputDevices[1].deviceId; } } this.decodeFromInputVideoFunc(firstDeviceId); }).catch(err => { this.tipShow = false; console.error(err); }); }, decodeFromInputVideoFunc(firstDeviceId) { this.codeReader.reset(); // 重置 this.scanText = ''; this.codeReader.decodeFromInputVideoDeviceContinuously(firstDeviceId, 'video', (result, err) => { this.tipMsg = '正在尝试识别...'; this.scanText = ''; if (result) { console.log('扫描结果', result); this.scanText = result.text; if (this.scanText) { this.tipShow = false; // 这部分接下去的代码根据需要,读者自行编写了 // this.$store.commit('app/SET_SCANTEXT', result.text); // console.log('已扫描的小票列表', this.$store.getters.scanTextArr); } } if (err && !(err)) { this.tipMsg = '识别失败'; setTimeout(() => { this.tipShow = false; }, 2000) console.error(err); } }); }, clickIndexLeft(){ // 返回上一页 this.codeReader = null; this.$destroy(); this.$router.back(); } } } </script>
<style lang="scss"> .scan-index-bar{ background-image: linear-gradient( -45deg, #42a5ff ,#59cfff); } .van-nav-bar__title{ color: #fff !important; } .scan-video{ height: 80vh; } .scan-tip{ width: 100vw; text-align: center; margin-bottom: 10vh; color: white; font-size: 5vw; } .page-scan{ overflow-y: hidden; background-color: #363636; } </style> |