• H5调用摄像头扫码详解


    思路

    第一步: 要能调起设备摄像头
    第二步:扫码
    第三步:解析二维码

    技术简介

    WebRTC API

    WebRTC (Web Real-Time Communications) 是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间 点对点(Peer-to-Peer) 的连接,实现视频流和(或)音频流或者其他任意数据的传输。WebRTC 包含的这些标准使用户在无需安装任何插件或者第三方的软件的情况下,创建 点对点(Peer-to-Peer) 的数据分享和电话会议成为可能。

    重要接口

    MediaStream接口:是一个媒体内容的流.。一个流包含几个轨道,比如视频和音频轨道。

    MDN : WebRTC API

    核心的API navigator.mediaDevices.getUserMedia

    MediaDevices 接口提供访问连接媒体输入的设备,如照相机和麦克风,以及屏幕共享等。它可以使你取得任何硬件资源的媒体数据。

    MediaDevices.getUserMedia() 会提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。此流可以包含一个视频轨道(来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等)、一个音频轨道(同样来自硬件或虚拟音频源,比如麦克风、A/D 转换器等等),也可能是其它轨道类型。

    它返回一个 Promise 对象,成功后会resolve回调一个 MediaStream 对象。若用户拒绝了使用权限,或者需要的媒体源不可用,promise会reject回调一个 PermissionDeniedError 或者 NotFoundError

    通常你可以使用 navigator.mediaDevices 来获取 MediaDevices ,例如:

    navigator.mediaDevices.getUserMedia(constraints)
    .then(function(stream) {
      /* 使用这个 stream stream */
    })
    .catch(function(err) {
      /* 处理 error */
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    语法

    var promise = navigator.mediaDevices.getUserMedia(constraints);
    
    • 1

    以下同时请求不带任何参数的音频和视频:

    MDN: MediaDevices

    二维码解析库 JSQR

    jsQR 是一个纯 JavaScript 二维码解析库,该库读取原始图像或者是摄像头,并将定位,提取和解析其中的任何 QR码。

    如果要使用 jsQR 扫描网络摄像头流,则需要 ImageData 从视频流中提取,然后可以将其传递给 jsQR。

    jsQR 导出一个方法,该方法接受 4 个参数,分别是解码的 图像数据,宽、高 以及 可选的对象 进一步配置扫描行为。

    imageData:格式为 [r0, g0, b0, a0, r1, g1, b1, a1, …] 的 Uint8ClampedArray( 8位无符号整型固定数组) 的 rgba 像素值。

    const code = jsQR(imageData, width, height, options);
    if (code) {
      console.log('找到二维码!', code);
    }
    
    • 1
    • 2
    • 3
    • 4

    github:jsQR

    requestAnimationFrame

    window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行.

    window.requestAnimationFrame(callback);
    
    • 1

    MDN: requestAnimationFrame

    代码实现

    流程: 浏览器进行调取摄像头,调用失败执行失败的回调;调用成功,进行捕获视频流,然后扫码识别,没有扫描到二维码就继续扫描, 扫码成功之后弹出扫码内容, 并停止扫码。

    初始化

     <div></div>
     <video src="" style="display: none"></video>
     <canvas></canvas>
    
    • 1
    • 2
    • 3
      // 捕获视频流
       var promise = navigator.mediaDevices.getUserMedia({ video: true })
         promise.then((mediaStream) => {
         	 // 成功的返回值mediaStream,既是媒体内容的流。
         	 // 通过原生video标签,展示视频
              document.querySelector('video').srcObject = mediaStream
              document.querySelector('video').play()
             // 调用tick之前, 这个时候已经可以看到页面上在展示我们录制到的内容
              tick()
          }).catch(function (err) {
              console.log(err, 'err')
          })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    // tick方法,初始化canvas,调用jsQR识别二维码
     function tick() {
           let cvs = document.querySelector('canvas')
           var ctx = cvs.getContext('2d')
           cvs.width = '800'
           cvs.height = '600'
           // 通过canvas绘制每一帧图片, 使绘制的图片和video展示的图片相吻合
           ctx.drawImage(document.querySelector('video'), 0, 0, '800', '600')
           // 获取画布数据(存储像素点信息)
           const imageData = ctx.getImageData(0, 0, '800', '600')
           let code = false
           // jsQR 识别二维码
           code = jsQR(imageData.data, imageData.width, imageData.height)
           if (code) {
           	  // 找到二维码,并展示在页面上
             document.querySelector('div').innerHTML = code.data
             // 绘制矩形框
              drawBox(code.location)
           }
           // 这次没有找到二维码,调用run方法
           run()
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
     function run() {
          if (!document.querySelector('div').innerHTML) {
          	// 进行周期性扫码识别
            requestAnimationFrame(tick)
          }
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可以看下, 这是扫码成功的回调。location 对象里包含了二维码具体的位置, 我们可以通过canvas划线,把页面中的码位置圈出来
    在这里插入图片描述

    function drawBox(location) {
            drawLine(location.topLeftCorner, location.topRightCorner)
            drawLine(location.topRightCorner, location.bottomRightCorner)
            drawLine(location.bottomRightCorner, location.bottomLeftCorner)
            drawLine(location.bottomLeftCorner, location.topLeftCorner)
          }
          function drawLine(begin, end) {
            let cvs = document.querySelector('canvas')
            var ctx = cvs.getContext('2d')
            ctx.beginPath()
            ctx.moveTo(begin.x, begin.y)
            ctx.lineTo(end.x, end.y)
            ctx.lineWidth = '3'
            ctx.strokeStyle = 'red'
            ctx.stroke()
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    最终实现效果就是这样

    到这里基本上就完事了, 可以调起摄像头, 可以正确扫码并拿到对应的值。

    参考文章: https://github.com/dragonir/h5-scan-qrcode
    JSQR: https://www.npmjs.com/package/jsqr
    https://blog.csdn.net/weixin_41856395/article/details/120597131

  • 相关阅读:
    用位运算实现加减乘除法
    什么是JSON
    简单排序(排序过程)
    回溯算法 解题思路
    VMware安装ubuntu之后共享文件夹不能显示的问题
    什么性格的人适合生物类专业?高考志愿填报选专业
    el-upload实现上传文件夹
    计算机网络 概述部分
    PHP基础与安全
    嵌入式Linux--进程间通讯--消息队列
  • 原文地址:https://blog.csdn.net/weixin_46034375/article/details/126542556