赞
踩
最开始公司说到这个需求的时候第一个想法是使用微信的 js SDK 接入微信的扫一扫。但是得知所做的这个功能模块是需要嵌入到之前开发的app中,所以就只有使用 js 原生的 API 来开发了。最后选用了 navigator.mediaDevices.getUserMedia
用来获取摄像头的视频流,通过 canvas
展示到页面上,同时获取到 canvas
生成的图片对象数据,传递个 jsQR
这个开源库进行二维码的解析。
功能开发完成之后我就在 uniapp 插件社区中,把我写的组件给发布了。发布之后大受欢迎,同时也收到了各种反馈,如:识别二维码速度过慢、识别范围小、能否有一直识别、可调用闪关灯吗等问题。后面一直在持续更新,完善了很多的功能。
之后换了一家公司也接到了类型的需求,但是没有选用 uniapp
这个技术栈,而是选用了 vue3+TS
的开发规范。没有办法只有把之前写好的组件进行了重写。
最近闲下来后,发现在网络上真正分享这个功能的文章比较少,要么说的不清不楚的,要么就还是在使用 navigator.getUserMedia
这个已经废除的 API 规范,要么就是不兼容苹果系统,还有就是大量复制的水文章。
所以决定写一篇文章来说说怎么实现这个功能,并且使用原生 js
再次重写了这个功能。一下代码都使用原生 js
进行讲解。同时提供 uniapp
、vue3+TS
、原生js
的 demo 下载。
当前只开发手机页面版,请使用手机直接扫码查看 demo 演示。闪光灯只有在安卓系统下谷歌内核浏览器中可以控制
闪光灯只有在安卓系统下谷歌内核浏览器中可以控制
闪光灯只有在安卓系统下谷歌内核浏览器中可以控制
其他环境我是默认隐藏按钮的
地址:https://h5plugin.mumudev.top/#/pages/getQrcode/getQrcode
navigator.mediaDevices.getUserMedia
用于唤起摄像头,并且获取到摄像头的视频流数据
文档地址:MediaDevices.getUserMedia()
此 API 必须要 HTTPS 环境才能调用,所以就必须要我们的测试环境与生成环境都配置 HTTPS,测试环境如果是 VUE 或者 UNIAPP 就直接在 DEV SERVER 中进行配置就可以了,原生的话推荐使用 VS CODE 中的 Live Server 插件生成假的证书进行配置与启动服务
"liveServer.settings.https": {
"enable": true,
"cert": "D:\\vsCode\\https\\example.com+5.pem", //本地证书地址
"key": "D:\\vsCode\\https\\example.com+5-key.pem", //本地密钥地址
"passphrase": ""
},
jsQR.js
用于对图片中的二维码进行解析
GitHub地址:cozmo/jsQR
一款大佬写的开源库。
canvas 标签
这个算是 HTML 基础吧
viod 标签
基础中的基础
这里就使用原生HTML写法给大家讲解当中的核心代码,完整的mode与其他框架的代码可以在上面小程序中进行下载
完整的mode中都是封装好的代码,可直接开箱即用。
// index.html
<div id="mumuQrcode">
<!-- canvas 用于展示视频流与识别到的二维码位置 -->
<canvas id='canvas'></canvas>
<!-- 中间的扫描框 -->
<div class="box">
<div class="line"></div>
<div class="angle"></div>
</div>
</div>
<!-- 先引入 jsQR 库,后引入自己写的代码 -->
<script src="./jsQR.js"></script>
<script src="./index.js"></script>
// index.js // 判断当前是否是 https 环境 if (origin.indexOf('https') === -1) throw '请在 https 环境中调用摄像头。' // 获取当前屏幕可用的宽高,用于设置全屏扫描 const windowWidth = document.documentElement.clientWidth const windowHeight = document.documentElement.clientHeight // 获取页面上的 canvas 组件,用上面获取的宽高来设置 canvas 为全屏,并且创建出 画布 const canvas = document.getElementById('canvas') canvas.width = windowWidth canvas.height = windowHeight // 创建画布 const canvas2d = canvas.getContext('2d') // 创建一个 video 标签,并且设置 video 的尺寸为全屏,用于接收摄像头的数据 const video = document.createElement('video') video.width = windowWidth video.height = windowHeight // 设置 video 标签不显示进度条 video.setAttribute('playsinline', 'true') video.setAttribute('webkit-playsinline', 'true') // 设置调用摄像头的参数 // 调用摄像头时的分辨率,摄像头的分辨率是以横屏计算的,所以要把屏幕的高度当作视频宽度,屏幕的宽度当作视频高度度,这样就可以获取到一个和手机物理分辨率一样的视频尺寸。这里是可以传递比分辨率高的数字,只要尺寸的比例是一样即可 const width = windowHeight const height = windowWidth const videoParam = { audio: false, video: { facingMode: { /** 使用后置还是前置摄像头 后:environment 前:user*/ exact: 'environment' }, width, // 刚刚设置的宽度 height // 刚刚设置的高度 } } // 调用摄像头。此 api 是一个 promise navigator.mediaDevices.getUserMedia(videoParam).then(stream => { // 回调中回返回 stream 获取到的视频流数据 // 把视频流数据给到 video 中 video.srcObject = stream // 播放 video video.play() // 调用下面写的解析函数 tick() }) // 解析函数,用于截取视频每一帧来解析二维码 function tick() { // 判断video中是否加载好视频流数据,只有加载好后才解析 if (video.readyState === video.HAVE_ENOUGH_DATA) { // 把视频的每一帧截取画到刚刚创建的画布上 canvas2d.drawImage(video, 0, 0, windowWidth, windowHeight) // 通过 canvas画布 获取到当前每一帧的 图片对象 数据 const imageData = canvas2d.getImageData(0, 0, windowWidth, windowHeight) // 把 图片对象 数据交给 jsQR 这个库进行解析 const codeRes = jsQR(imageData.data, imageData.width, imageData.height, { inversionAttempts: 'dontInvert' }) // 判断是否获取到二维码数据 if (codeRes && codeRes.data) { // codeRes.data 中就是二维码中的数据了 console.log(codeRes.data) alert(codeRes.data) } } // 每当这个函数执行完后,马上就执行这个函数 // requestAnimationFrame 这个方法可以百度一下 requestAnimationFrame(tick) }
进测试。发现现代中的浏览器都支持,微信内部与跨平台app中也完美调用。
不支持的浏览器有:
苹果系统下的 阿里系浏览器(UC、夸克等)
使用的 Webkit 内核较低,而且还会抓取视频流数据,导致无法正常预先
open 浏览器
不知道具体原因,听说国内的open没有采用他自己的内核
IE
IE就不多说了
附带一张兼容图
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。