• web实现usb扫码枪读取二维码数据功能



    前言

    常见的扫码枪有2种,一种串口扫码枪通过串口实现数据通信,技术实现上使用Web Serial API,可以查看本人另一篇博文Web Serial API串口通信,实现web和electron扫码枪读取数据。另一种是usb扫码枪,也是本文所要讲述的,usb扫码枪可以看成是输入设备,实际等同于键盘通过按键输入方式进行二维码信息读取,技术实现相比串口扫码枪简单多了,只需监听输入事件,但是它也有很多弊端和限制。


    一、usb扫码枪获取数据步骤和条件

    1.必须有个input输入框并且让其处于聚焦状态
    2.监听输入框按键事件keydown或者keyup,遇到keyCode为13(回车)表示输入结束(一般usb扫码枪设置读取以回车结束,当然有的也可以设置其他,如果是其他结束符就监听相应的keyCode)
    3.回车结束后从input输入框dom对象获取value值就是二维码内容

    通过上面总结很容易实现,如下代码:

     <input id="input"/>    
    
    • 1
      <script>
       const input= document.getElementById('input');
       input.addEventListener('keydown',e=>{
         if(e.keyCode==13){//回车结束
            let qrcodeData=input.value;
            console.log(`二维码数据为${qrcodeData}`)
         }
       })
       input.focus()//聚焦
      </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    扫码测试:
    在这里插入图片描述

    二、优化

    上面demo我们只是简单实现了从扫码枪获取数据功能的核心逻辑,但实际开发中我们的需求场景可能不一样,会遇到很多问题和坑点需要优化解决。

    挖坑1:中文输入法会影响输入内容准确性,可以通过设置input type=“password”,设置输入框为密码类型解决

    挖坑2:如果你的需求是读取二维码内容填充到一个特定输入框可以像上面例子一样实现,但是如果读取二维码不需要填充到输入框或者需要回显很多个地方就需要先获取数据再做数据处理,这个时候可以投机取巧画个输入框移动到屏幕外。

    挖坑3:当你采用挖坑2技巧把输入框画在屏幕外,并用代码聚焦在输入框后,用户可能乱点页面导致读取数据前失焦,从而无法正常读取到二维码数据。解决方法可以定时不间断自动对焦并显示加载模态框引导用户不去乱点击,减少失败率。

    二、代码实现

    接下来将以vue3 elementui-plus来举例,实现一个扫码获取多个表单字段回显功能

    操作逻辑设计:
    1.页面设计一个二维码识别按钮
    2.用户点击按钮触发扫码功能并锁定界面出现识别中加载转圈,防止用户乱点击失焦。
    3.扫码识别,识别成功,识别加载转圈消失,数据自动回显到对应表单控件上
    4.n秒内如果用户未扫码关闭加载转圈恢复初态

    代码如下(示例):

    scanner.js(扫码逻辑封装hook):

    import { nextTick, onMounted } from "vue";
    import { ElLoading, ElMessage } from "element-plus";
    
    let isInputing = false; //是否在输入中
    
    /**
     *
     * @param {*} inputRef:输入框Ref
     * @param {*} callBack :识别回调
     * @returns
     */
    const useScancer = (inputRef, callBack) => {
      let interval = null; //定时器实例
      let loading = null; //ElLoading组件实例
    
      //输入框绑定keydown事件
      const addKeydownEvent = () => {
        if (inputRef?.value) {
          inputRef.value.addEventListener("keydown", (event) => {
            isInputing = true;
            let keyCode = event.keyCode || event.which;
            if (keyCode === 13) {
              let qrcodeData = inputRef?.value?.value ?? ""; //二维码数据
              if (interval) {
                clearInterval(interval);
                interval = null;
              }
              callBack(qrcodeData);
              inputRef.value.value = "";
              loading && loading.close();
              loading = null;
              ElMessage.success("识别成功");
              isInputing = false;
            }
          });
        }
      };
    
      //自动聚焦
      const autoFocus = () => {
        //立即执行
        inputFocus();
        if (!interval) {
        //每50ms自动对焦
          interval = setInterval(() => {
            inputFocus();
          }, 50);
        }
      };
    
      //input聚焦
      const inputFocus = () => {
        if (inputRef && inputRef.value) {
          inputRef.value.focus();
        }
      };
    
      //开始扫码
      const handleScanCode = () => {
        if (!loading) {
          console.log('loading')
          loading = ElLoading.service({
            lock: true,
            text: "请开始扫码识别",
            background: "rgba(255, 255, 255, 0.7)",
          });
    
          //20秒内未扫码关闭loading
          setTimeout(() => {
            if (!isInputing) {
              loading && loading.close();
              loading = null;
              if (interval) {
                clearInterval(interval);
                interval = null;
              }
            }
          }, 20 * 1000);
    
          nextTick(() => {
            autoFocus();
          });
        }
      };
    
      //初始化绑定事件
      onMounted(() => {
        addKeydownEvent();
      });
    
      return { autoFocus, handleScanCode };
    };
    
    export default useScancer;
    
    
    • 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

    页面调用
    index.vue

    <template>
      <input class="input" ref="inputRef" type="password"/>
      <el-button @click.native="handleScanCode" type="primary">扫码识别</el-button>
      <el-form :model="formData" style="margin-top: 20px">
        <el-form-item label="姓名">
          <el-input disabled v-model="formData.name" />
        </el-form-item>
        <el-form-item label="年龄">
          <el-input disabled v-model="formData.age" />
        </el-form-item>
        <el-form-item label="生日">
          <el-input disabled v-model="formData.birthday" />
        </el-form-item>
      </el-form>
    </template>
    
    <script setup>
    import { ref, reactive } from "vue";
    import useScancer from "./scanner.js"; //扫码识别hook
    
    //表单数据
    const formData = reactive({
      name: "",
      age: "",
      birthday: "",
    });
    
    
    //输入框
    const inputRef = ref();
    
    //扫码回调
    const { handleScanCode } = useScancer(inputRef, (data) => {
      console.log(data, "二维码数据为:data");
      if (data) {
        try {
        //二维码字符串转为json对象
          data= JSON.parse(data);
          for(let key in formData){
            formData[key]=data[key]??'';
          }
        } catch (e) {
          console.log(e,'e')
        }
      }
    });
    </script>
    <style scoped>
    .input {
      position: fixed;
      left: 0;
      transform: translateX(-200%);
    }
    </style>
    
    
    • 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

    测试运行:

    初始态:
    在这里插入图片描述
    开始识别:
    在这里插入图片描述

    识别完成:
    在这里插入图片描述


    总结

    通过上面介绍可以看出usb扫码枪虽然使用简单,但是限制多,只能满足一些特定场景,对用户一些不确定操作可能会影响读取成功率,这个时候也可以从产品设计上引导用户主动触发聚焦减少用户乱点击频率,提高成功率。

  • 相关阅读:
    物流企业的“海外战事”
    2.10 XGBoost模型数学层面的理解(下篇)
    观察者模式有人会这个不,看来看去不会,第一次学这个看书也看不知道
    监控 5 分钟抓拍一次人脸,不够 89 次算旷工,居家办公员工:不敢去厕所
    LeetCode 162. 寻找峰值
    多环境 Flink Job 的生成方案(Maven)
    PIE-engine 教程 ——新疆石河子市棉花种植面积提取(阈值法)案例分析
    Salesforce-Visualforce-6.静态资源(Static Resources)
    基础知识笔记:协程基础元素
    从零开始!Jupyter Notebook的安装教程
  • 原文地址:https://blog.csdn.net/sd1sd2/article/details/137058825