在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
背景不久前我做了关于获取浏览器摄像头并扫码识别的功能,本文中梳理了涉及到知识点及具体代码实现,整理成此篇文章内容。 本文主要介绍,通过使用基于 实现效果本实例中主要有两个页面首页和扫码页,具体实现效果如下图所示。
在线体验:https://dragonir.github.io/h5-scan-qrcode 提示:需要在有摄像头设备的浏览器中竖屏访问。手机横竖屏检测小知识可前往我的另一篇文章《五十音小游戏中的前端知识》 中进行了解。 技术简介WebRTC APIWebRTC (Web Real-Time Communications) 是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间 三个主要接口:
WebRTC adapter虽然
核心的API 网页调用摄像头需要调用 它返回一个 通常可以使用 navigator.mediaDevices.getUserMedia(constraints) .then(function(stream) { // 使用这个stream }) .catch(function(err) { // 处理error })
二维码解析库
如果要使用
const code = jsQR(imageData, width, height, options); if (code) { console.log('找到二维码!', code); }
代码实现流程整个扫码流程如下图所示:页面初始化,先检查浏览器是否支持 下文内容对流程进行拆分,分别实现对应的功能。 扫码组件 页面结构我们先看下页面结构,主要由
<template> <div class="scaner" ref="scaner"> <!-- 提示框:用于在不兼容的浏览器中显示提示语 --> <div class="banner" v-if="showBanner"> <i class="close_icon" @click="() => showBanner = false"></i> <p class="text">若当前浏览器无法扫码,请切换其他浏览器尝试</p> </div> <!-- 扫码框:显示扫码动画 --> <div class="cover"> <p class="line"></p> <span class="square top left"></span> <span class="square top right"></span> <span class="square bottom right"></span> <span class="square bottom left"></span> <p class="tips">将二维码放入框内,即可自动扫描</p> </div> <!-- 视频流显示 --> <video v-show="showPlay" class="source" ref="video" :width="videoWH.width" :height="videoWH.height" controls ></video> <canvas v-show="!showPlay" ref="canvas" /> <button v-show="showPlay" @click="run">开始</button> </div> </template> 方法:绘制
// 画线 drawLine (begin, end) { this.canvas.beginPath(); this.canvas.moveTo(begin.x, begin.y); this.canvas.lineTo(end.x, end.y); this.canvas.lineWidth = this.lineWidth; this.canvas.strokeStyle = this.lineColor; this.canvas.stroke(); }, // 画框 drawBox (location) { if (this.drawOnfound) { this.drawLine(location.topLeftCorner, location.topRightCorner); this.drawLine(location.topRightCorner, location.bottomRightCorner); this.drawLine(location.bottomRightCorner, location.bottomLeftCorner); this.drawLine(location.bottomLeftCorner, location.topLeftCorner); } }, 方法:初始化
// 初始化 setup () { // 判断了浏览器是否支持挂载在MediaDevices.getUserMedia()的方法 if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { this.previousCode = null; this.parity = 0; this.active = true; this.canvas = this.$refs.canvas.getContext("2d"); // 获取摄像头模式,默认设置是后置摄像头 const facingMode = this.useBackCamera ? { exact: 'environment' } : 'user'; // 摄像头视频处理 const handleSuccess = stream => { if (this.$refs.video.srcObject !== undefined) { this.$refs.video.srcObject = stream; } else if (window.videoEl.mozSrcObject !== undefined) { this.$refs.video.mozSrcObject = stream; } else if (window.URL.createObjectURL) { this.$refs.video.src = window.URL.createObjectURL(stream); } else if (window.webkitURL) { this.$refs.video.src = window.webkitURL.createObjectURL(stream); } else { this.$refs.video.src = stream; } // 不希望用户来拖动进度条的话,可以直接使用playsinline属性,webkit-playsinline属性 this.$refs.video.playsInline = true; const playPromise = this.$refs.video.play(); playPromise.catch(() => (this.showPlay = true)); // 视频开始播放时进行周期性扫码识别 playPromise.then(this.run); }; // 捕获视频流 navigator.mediaDevices .getUserMedia({ video: { facingMode } }) .then(handleSuccess) .catch(() => { navigator.mediaDevices .getUserMedia({ video: true }) .then(handleSuccess) .catch(error => { this.$emit("error-captured", error); }); }); } }, 方法:周期性扫描 run () { if (this.active) { // 浏览器在下次重绘前循环调用扫码方法 requestAnimationFrame(this.tick); } }, 方法:成功回调 // 二维码识别成功事件处理 found (code) { if (this.previousCode !== code) { this.previousCode = code; } else if (this.previousCode === code) { this.parity += 1; } if (this.parity > 2) { this.active = this.stopOnScanned ? false : true; this.parity = 0; this.$emit("code-scanned", code); } }, 方法:停止 // 完全停止 fullStop () { if (this.$refs.video && this.$refs.video.srcObject) { // 停止视频流序列轨道 this.$refs.video.srcObject.getTracks().forEach(t => t.stop()); } } 方法:扫描
// 周期性扫码识别 tick () { // 视频处于准备阶段,并且已经加载足够的数据 if (this.$refs.video && this.$refs.video.readyState === this.$refs.video.HAVE_ENOUGH_DATA) { // 开始在画布上绘制视频 this.$refs.canvas.height = this.videoWH.height; this.$refs.canvas.width = this.videoWH.width; this.canvas.drawImage(this.$refs.video, 0, 0, this.$refs.canvas.width, this.$refs.canvas.height); // getImageData() 复制画布上制定矩形的像素数据 const imageData = this.canvas.getImageData(0, 0, this.$refs.canvas.width, this.$refs.canvas.height); let code = false; try { // 识别二维码 code = jsQR(imageData.data, imageData.width, imageData.height); } catch (e) { console.error(e); } // 如果识别出二维码,绘制矩形框 if (code) { this.drawBox(code.location); // 识别成功事件处理 this.found(code.data); } } this.run(); }, 父组件
页面结构 <template> <div class="scan"> <!-- 页面导航栏 --> <div class="nav"> <a class="close" @click="() => $router.go(-1)"></a> <p class="title">Scan QRcode</p> </div> <div class="scroll-container"> <!-- 扫码子组件 --> <Scaner v-on:code-scanned="codeScanned" v-on:error-captured="errorCaptured" :stop-on-scanned="true" :draw-on-found="true" :responsive="false" /> </div> </div> </template> 父组件方法 import Scaner from '../components/Scaner'; export default { name: 'Scan', components: { Scaner }, data () { return { errorMessage: "", scanned: "" } }, methods: { codeScanned(code) { this.scanned = code; setTimeout(() => { alert(`扫码解析成功: $[code]`); }, 200) }, errorCaptured(error) { switch (error.name) { case "NotAllowedError": this.errorMessage = "Camera permission denied."; break; case "NotFoundError": this.errorMessage = "There is no connected camera."; break; case "NotSupportedError": this.errorMessage = "Seems like this page is served in non-secure context."; break; case "NotReadableError": this.errorMessage = "Couldn't access your camera. Is it already in use?"; break; case "OverconstrainedError": this.errorMessage = "Constraints don't match any installed camera."; break; default: this.errorMessage = "UNKNOWN ERROR: " + error.message; } console.error(this.errorMessage); alert('相机调用失败'); } }, mounted () { var str = navigator.userAgent.toLowerCase(); var ver = str.match(/cpu iphone os (.*?) like mac os/); // 经测试 iOS 10.3.3以下系统无法成功调用相机摄像头 if (ver && ver[1].replace(/_/g,".") < '10.3.3') { alert('相机调用失败'); } } 完整代码
总结应用扩展我觉得以下几个功能都是可以通过浏览器调用摄像头并扫描识别来实现的,大家觉得还有哪些
兼容性
参考资料 [1]. Taking still photos with WebRTC [2]. Choosing cameras in JavaScript with the mediaDevices API 作者:dragonir 本文地址:https://www.cnblogs.com/dragonir/p/15405141.html 到此这篇关于Vue实现浏览器端扫码功能的文章就介绍到这了,更多相关vue浏览器扫码内容请搜索极客世界以前的文章或继续浏览下面的相关文章希望大家以后多多支持极客世界! |
请发表评论