通过h5-input[camera] 调用相机进行拍照成图片,通过条形码等识别库 解析出 数据;
<input @change="toQR" type="file" accept="image/*" capture="camera">
1-1.方案一:input+ Quagga识别库,success, 可识别 条形码,不能识别二维码;
quagga库地址 https://www.npmjs.com/package/quagga
npm install quagga --save
jsqr库地址 https://www.npmjs.com/package/jsqr
npm install jsqr --save
例: 1.正方形不带其它无效内容 只二维码的图片,设置jsqr识别长宽 100*100 ,可成功识别
2.长方形带其它无效内容,比如拍照出来的二维码图片,设置jsqr识别长宽 自适(传入图片的长宽 或100*100),都识别失败
qrcode.decode(img)
调用手机原生摄像头 动态识别 用相关支持js库 解析 二维码 条形码
zxing-js/library库地址 https://github.com/zxing-js/library
npm install @zxing/library --save
vue-demo
<template>
<div class="mt46 input-cells">
<div class="flex a-center f-fl">
<div>车架号</div>
<input v-model="queryParams.vin" class="cell-input ml5 mr10" type="text"
placeholder="请输入或扫描拍照" />
</div>
<div class="flex l-center a-left">
<div class="qr-item mr5 ml5">
<!--图标自用 https://www.iconfont.cn/ -->
<!--<i class="iconfont icon-qr f18 c-ffaa09"></i>-->
<img src="#" width="20" height="20" alt="">
<input v-if="isUploadBarCode" class="qr-item-input" @change="toQR" type="file" accept="image/*" capture="camera">
</div>
</div>
</div>
</template>
<style scoped>
.input-cells{
display: flex;
justify-content:space-between;
align-items: center;
position: relative;
background-color: #fff;
overflow: hidden;
padding: .06rem .15rem;
height: .32rem;
line-height: .32rem;
font-size: .14rem;
}
.input-label{
margin-left: 0;
font-size: .14rem;
width: .9rem;
}
.input-cells>input{
font-size: .14rem;
text-align: right;
}
.input-cells>img{
width: .06rem;
height: .1rem;
margin-right: .03rem;
}
.qr-item{
width: .3rem;
height: 100%;
background-size: 120%;
position: relative;
overflow: hidden;
border: 1px solid #dae7f6;
background-color: #f5f5f5;
}
.qr-item-input{
opacity: 0;
width: 100%;
height: 100%;
background-size: 100%;
position: absolute;
top: 0;
left: 0;
}
/*common*/
.flex{
display: flex;
}
.a-center{
align-items: center;
}
.f-fl{
float: left;
}
.ml5{
margin-left: .05rem;
}
.mr10{
margin-right: .1rem;
}
</style>
es6引入使用
import Quagga from 'quagga'
使用
<script type="text/ecmascript-6">
import Quagga from 'quagga'
export default {
name: '',
data() {
return {
queryParams: {
vin: null
},
isUploadBarCode: true, // 控制销毁
}
},
mounted() {
},
methods: {
// 图片 识别 条形码
toQR(e) {
const that = this
const file0 = e.target.files[0]
// console.log('toQR()-file0', file0)
this.isUploadBarCode = false
Quagga.decodeSingle({
inputStream: {
name : 'image',
type : 'ImageStream',
// size: 1600, // 这里指定图片的大小,需要自己测试一下
singleChannel: false,
},
locator: {
patchSize: 'medium',
halfSample: false
},
numOfWorkers: 1,
decoder: { // ean_reader 这里指定ean条形码,就是国际13位的条形码 code39 code_128
readers: ['code_128_reader']
},
// readers: ['code_128_reader'],
locate: true,
src: URL.createObjectURL(file0)
},(result) => {
console.log('Quagga()-result', result)
// let code = result.codeResult.code
if (result && result.codeResult) {
that.queryParams.vin = result.codeResult.code
// 执行 页面请求刷新
} else {
that.queryParams.vin = null
console.warn('识别失败,请手动输入')
}
this.isUploadBarCode = true
})
},
}
}
</script>
方案二 html+css 公用与 方案一
qrcodeSearch.js
// import QRCode from '../libs/qr/qrcode'
import jsqr from 'jsqr'
// 二维码 或 条形码 识别
export function showQrCode(file, params, callback) {
const ready = new FileReader()
/* 开始读取指定的Blob对象或File对象中的内容. 当读取操作完成时,readyState属性的值会成为DONE,如果设置了onloadend事件处理程序,则调用之.同时,result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容.*/
ready.readAsDataURL(file) // 调用reader.readAsDataURL()方法,把图片转成base64
ready.onload = function() {
const re = this.result
canvasDataURL(re, params, callback)
}
}
function canvasDataURL(path, obj, callback) {
const img = new Image()
img.src = path
// 生成canvas
const canvas = document.createElement('canvas')
// const canvas = document.getElementById('qrcanvas')
const ctx = canvas.getContext('2d')
// const _this = this
img.onload = function() {
console.log('canvasDataURL()-img', img.height, img.width)
// let w = img.width
// let h = img.height
const w = 100
const h = 100
ctx.clearRect(0, 0, w, h)
ctx.drawImage(img, 0, 0, w, h);
const imageData = ctx.getImageData(0, 0, w, h);
const code = jsqr(imageData.data, w, h);
const res = {
data: null,
message: '识别成功',
code: 0,
}
if(code){
res.data = code.data
callback(res)
}else{
res.code = -1
res.data = null
res.message = '识别失败'
callback(res)
}
}
}
使用
<script type="text/ecmascript-6">
import * as QrCode from './qrcodeSearch'
export default {
name: '',
data() {
return {
queryParams: {
vin: null
},
isUploadBarCode: true, // 控制销毁
}
},
mounted() {
},
methods: {
// 图片 识别 条形码
toQR(e) {
const that = this
const file0 = e.target.files[0]
// console.log('toQR()-file0', file0)
this.isUploadBarCode = false
QrCode.showQrCode(file0, {}, function (res) {
this.isUploadBarCode = true
console.log('showQrCode()-res', res)
if (res.code === 0) {
that.queryParams.vin = res.data
} else {
console.warn('识别失败,请手动输入')
}
})
},
}
}
</script>
注意,因为调用原生摄像头 要在 https环境下,故需要把代码发布到 带域名(https)的测试环境 进行调试【这是必要的前提条件,没有测试环境就不要整了】。故而代码调整要反复发布构建代码,不过楼主的这个例子已经是整好了的;
vue-demo
<template>
<div class="page bgc-f4f4f4">
<!--路由返回-->
<!--<lisa-header title="扫描二维码"></lisa-header>-->
<video ref="video" id="video" class="video vjs-fluid" autoplay></video>
<div v-show="tipShow" class="tip">{{tipMsg}}</div>
</div>
</template>
<style scoped>
/*vjs-fluid 自适video 长宽*/
.video{
/*border: 1px solid gray;*/
margin-top: .5rem;
/*width: 2.6rem;*/
/*height: 3rem;*/
}
.tip{
color: white;
font-size: .16rem;
}
/* common */
.bgc-f4f4f4{
background-color: #363636;
}
.page{
overflow-y: auto;
position: relative;
}
</style>
使用
<script type="text/ecmascript-6">
import { MessageBox } from 'mint-ui'
import { BrowserMultiFormatReader } from '@zxing/library'
export default {
name: 'qr-code-search',
components: {},
data() {
return {
loadingShow: false,
codeReader: new BrowserMultiFormatReader(),
textContent: null,
vin: null,
tipMsg: '正在尝试识别....',
tipShow: false
}
},
created() {
// this.tipShow = true
this.openScan()
},
methods: {
async openScan() {
const that = this
that.codeReader.getVideoInputDevices().then((videoInputDevices) => {
that.tipShow = true
that.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;
}
}
that.decodeFromInputVideoFunc(firstDeviceId)
}).catch((err) => {
that.tipShow = false
console.error(err);
});
},
async openScanTwo() {
const that = this
// codeReader.reset() // 重置
// that.textContent = null // 重置
that.codeReader = await new BrowserMultiFormatReader()
that.codeReader.getVideoInputDevices().then((videoInputDevices) => {
that.tipShow = true
that.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;
}
}
that.decodeFromInputVideoFunc(firstDeviceId)
}).catch((err) => {
that.tipShow = false
console.error(err);
});
},
decodeFromInputVideoFunc(firstDeviceId) {
const that = this
that.codeReader.reset() // 重置
that.textContent = null // 重置
that.codeReader.decodeFromInputVideoDeviceContinuously(firstDeviceId, 'video', (result, err) => {
that.tipMsg = '正在尝试识别...'
// let nowContent = null
that.textContent = null
if (result) {
console.log(result);
that.textContent = result.text;
if (that.textContent) {
that.tipShow = false
that.msgBoxFunc(that.textContent)
}
}
if (err && !(err)) {
that.tipMsg = '识别失败'
setTimeout(() => {
that.tipShow = false
},2000)
console.error(err);
}
});
},
// that.$createDialog 是 cube-ui滴滴 messageBox ,到这一步 二维码值已出,这里是可有可无的代码块,看各自项目ui使用情况 自行替换 messageBox
msgBoxFunc(textContent) {
const that = this
// alert('8执行了 msgBoxFunc(),textContent:' + textContent)
// cube-ui messageBox
that.$createDialog({
type: 'prompt',
// icon: 'cubeic-alert',
title: '识别内容',
// content: that.textContent,
prompt: {
value: textContent,
placeholder: '请输入'
},
confirmBtn: {
text: '确定内容',
active: true,
disabled: false,
href: 'javascript:;'
},
cancelBtn: {
text: '继续识别',
active: false,
disabled: false,
href: 'javascript:;'
},
onConfirm: (e, promptValue) => {
// that.hide()
console.log('onConfirm: ()')
that.vin = promptValue
},
onCancel: () => {
console.log('onCancel: ()')
that.$nextTick(()=>{
that.openScanTwo()
})
}
}).show()
},
// msgBoxFunc2() 整块代码是mint-ui messageBox示例用,此处未使用,可直接注释;
msgBoxFunc2(textContent) {
// mint-ui messageBox 有重复 inputValue值问题,新调用 显示是旧的值
const that = this
// alert('8执行了 msgBoxFunc(),textContent:' + textContent)
MessageBox.prompt('识别内容', {
inputValidator: (val) => {
if (val === null) {
return true;//初始化的值为null,不做处理的话,刚打开MessageBox就会校验出错,影响用户体验
}
},
confirmButtonText: '确定内容',
cancelButtonText: '继续识别',
inputValue: textContent,
}).then(({value, action}) => {
if (action === 'confirm') {
that.vin = value
}
if (action === 'cancel') {
that.$nextTick(()=>{
that.openScanTwo()
})
}
}).catch(err => {
console.log(err);
})
}
}
}
</script>
道路过程是曲折坎坷的,结果是良好的,以为整不出来的然后柳暗花明;
参考文章:
https://www.jianshu.com/p/30a34157c7d1 zing-js/library
https://blog.csdn.net/aoshilang2249/article/details/105222706 MediaDevices.getUserMedia undefined 的问题
https://blog.csdn.net/yingzhi3104/article/details/105557591 quagga识别条形码图片
https://blog.csdn.net/qq_37705048/article/details/79816438 qrcode.js的识别
https://blog.csdn.net/haiyang5233233/article/details/105874129 调用摄像头是提示 navigator.mediaDevices.getUserMedia
https://blog.csdn.net/weixin_30260399/article/details/96458034 调用后置摄像头问题