纯js浏览器h5调用摄像头扫描识别解析 条形码+二维码

一、场景

  • 手机移动端-原生js 浏览器h5 解决 识别二维码 条形码功能;
  • 不借助Hbuilder.需要自己打包成APP,比如用Hbuilder打包,浏览器端项目h5 无打包成app部署 X 不采用
  • 不借助微信扫一扫,调用微信js-jdk有多麻烦,还要认证服务号,也不适用 其它浏览器打开 X 不采用

二、思路

1.思路一:input(相机拍照)+ 条形码或二维码识别js支持库

通过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

1-2.方案二:input+jsqr识别库,success,可识别条形码,也可识别二维码,但二维码识别很局限性,如果只做条形码识别可采用,若是识别二维码不适用;

jsqr库地址 https://www.npmjs.com/package/jsqr

npm install jsqr --save

例: 1.正方形不带其它无效内容 只二维码的图片,设置jsqr识别长宽 100*100 ,可成功识别

       2.长方形带其它无效内容,比如拍照出来的二维码图片,设置jsqr识别长宽 自适(传入图片的长宽 或100*100),都识别失败

1-3.方案三: input+qrcode库, 失败

qrcode.decode(img)

2.思路二:调用摄像头(video动态识别)+ 条形码或二维码识别js支持库

调用手机原生摄像头 动态识别 用相关支持js库  解析 二维码 条形码

2-1.方案四:video+ zxing识别库,success,可识别 条形码 可识别二维码,完美解决,楼主采用的这种;

 zxing-js/library库地址 https://github.com/zxing-js/library

npm install  @zxing/library --save

三、实例

1-1.方案一

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>

 

1-2.方案二

方案二 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>

2-1.方案四

  注意,因为调用原生摄像头 要在 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 调用后置摄像头问题

  • 11
    点赞
  • 99
    收藏
    觉得还不错? 一键收藏
  • 17
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值