概述:在Vue中调用摄像头拍照进行银行卡识别。

流程:vue中调用媒体摄像头(无前后摄像头切换功能)——>根据规定的框(前端自己定制)放置银行卡——>将识别的图片通过接口传到后台,后台识别出返回给前端银行卡的相关数据。

原理:调用媒体摄像头,应用的是navigator.mediaDevices.getUserMedia功能(API网站)调用摄像头拍摄视频流,将录像流放到video中,再绘制成canvas图像,将此刻的canvas图像生成base64位图片返回并存放到image中(存放到image中后,后续暂时没有用到,暂时只做存放用),定时将此刻返回的图片通过接口传输到后台识别,直到识别成功。

步骤:

注:代码为手打,已检查,可能还会存在单词拼写方面的问题,如报错,请检测拼写。

一. 调用媒体对象打开摄像头拍摄的相关函数封装(参考了网上的一些方法,之后查看了官网进行了优化)

新建js文件:Camera.js

/**
* 组件: 调用摄像头拍摄的构造函数
* @params {Object} options 参数如下:
*             video {DOM} video元素
*             width {Number} 画布宽度
*             height {Number} 画布高度
*             onShoot {Function} 录像回调函数
*             onError {Function} error回调函数
*         {Object} canvasOption 参数如下:
*             marginTop {Number} 银行卡可视区域距离屏幕顶部的距离,用作画布坐标
*             marginLeft {Number} 银行卡可视区域距离屏幕左侧的距离,用作画布坐标
*             width {Number} 银行卡可视区域宽度
*             height {Number} 银行卡可视区域高度
*调用:
*    Camera.create(options, canvasOption)
*/
function Camera(options, canvasOption) {this.video = options.video;this.width = options.width || 640;this.height = options.height || 480;this.shadeMarginTop = canvasOption.marginTop;this.shadeMarginLeft = canvasOption.marginLeft;this.shadeWidth = canvasOption.width;this.shadeHeight = canvasOption.height;this.onError = options.onError;this.onShoot = options.onShoot;this.mediaStreamTrack = null; // 存放视频流
}Camera.prototype = {init: function() {if(navigator.mediaDevices === undefined) {navigator.mediaDevices = {}}// 一些浏览器部分支持 mediaDevices。 我们不能直接给对象设置 getUserMedia// 因为这样可能会覆盖已有的属性。这里我们只会在没有getUserMedia属性的时候添加它。if (navigator.mediaDevices.getUserMedia === undefined) {navigator.mediaDevices.getUserMedia = function(constraints) {// 首先,如果有getUserMedia的话,就获取它var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;// 一些浏览器根本没实现它 — 那么就返回一个error到promise的reject来保持一个统一的接口if (!getUserMedia) {return Promise.reject(new Error('getUserMedia is not implemented in this browser'));}// 否则,为老的navigator.getUserMedia方法包裹一个Promisereturn new Promise(function(resolve, reject) {getUserMedia.call(navigator, constraints, resolve, reject);});}}this.video.autoplay = 'true';this.canvasDom = document.createElement('canvas');this.canvasDom.width = this.width; this.canvasDom.height = this.height;this.canvasDom.style.display = 'none';document.querySelector('body').appendChild(this.canvasDom);},// 检查摄像头是否可用canCameraUse: function() {return (navigator.mediaDevices.getUserMedia && window.URL);},// 获取录像流到video中shoot: function() {var self = this;var video = self.video;if (self.canCameraUse) {navigator.mediaDevices.getUserMedia({video: { facingMode: { exact: "environment"} } // 后置摄像头设置}).then(function(stream) {self.mediaStreamTrack = stream// 旧的浏览器可能没有srcObjectif ("srcObject" in video) {video.srcObject = stream;}else {// 防止在新的浏览器里使用它,因为它已经不再支持了video.src = window.URL.createObjectURL(stream);}video.onloadedmetadata = function(e) {video.play();};video.addEventListener('error', function(error) {stream.stop();self.onError && self.onError(error);}, false);}).catch(function(err) {self.onError && self.onError(err)})}},// 将录像绘制到canvasdrawVideo: function() {var canvasDom = this.canvasDom;var ctx = canvasDom.getContext('2d');ctx.fillStyle = "#000000";// 视频可能不会充满屏幕,这时候,需要按照视频缩放的比例来剪切所需要的部分let videoHeight = this.video.videoHeight // 视频本身的高度let videoWidth = this.video.videoWidth // 视频本身的宽度let videoWidthScale = 1if (videoWidth && this.width !== videoWidth) { // 计算当前视频的缩放比例videoWidthScale = videoWidth / this.width}let newHeight = videoHeight / videoWidthScale // 当前显示的视频高度let videoMarginTop = this.video.clientHeight - newHeight > 0 ?(this.video.clientHeight - newHeight) / 2 : 0 // 当前视频距离顶部高度let x = Math.floor(this.shadeMarginLeft * videoWidthScale) // 计算剪切视频的x坐标let y = Math.floor((this.shadeMarginTop - videoMarginTop) * videoWidthScale) // 计算剪切视频的y坐标let w = videoWidth - x * 2, h = Math.ceil(videoHeight - y * 2) // 计算剪切视频的宽高ctx.fillRect(this.shadeMarginLeft, this.shadeMarginTop, this.shadeWidth, this.shadeHeight); // 填充画布ctx.drawImage(this.video, x, y, w, h, this.shadeMarginLeft, this.shadeMarginTop, this.shadeWidth, this.shadeHeight) // 剪切视频并绘制成图片this.ctx = ctx},// 录像事件绑定addShootEvent: function() {let self = this;let video = self.video;// 正在录像video.addEventListener('timeupdate', function() {self.drawVideo();self.onShoot && self.onShoot()}, false)},// 将录像成图片snapshot: function(cb, imageType) {let self = thislet canvas = self.canvasDom;imageType = imageType || 'jpeg';let imageSrc = canvas.toDataURL('image/' + imageType);cb && cb(imageSrc);},// 开始录像play: function() {this.video.play();},// 停止录像pause: function() {this.video.pause();let self = this// 关闭摄像头self.mediaStreamTrack.getVideoTracks().forEach(function(track) {track.stop();self.ctx.clearRect(0, 0, self.canvasDom.width, self.canvasDom.height); // 清除画布});},render: function() { // 初始化this.init();this.shoot();this.drawVideo();this.addShootEvent();}};Camera.create = function(options, canvasOption) {var camera = new Camera(options, canvasOption.canvasStyle);camera.render();return camera;
};export default Camera

二. 在Vue中写识别银行卡界面:

1. HTML(img为触发图片,点击img触发银行卡识别来调用摄像头):

<img src=" ./assets/scan.svg" @click="scanBank" /><!-- 扫描界面html -->
<div class="camera" v-show="isScan"><!-- 遮罩层 --><canvas class="shade" id="canvasMaskShade" ref="videoShade"></canvas><!-- 视频存放处 --><video id="video" class="video"></video><!-- 取消识别按钮 --><button id="shootBtn" @click="cancelScan" class="shootBtn"><span>取消</span></button><!-- 存放扫描的图片 --><div id="imageBox"></div>
</div>

2. 样式(Less,层级(z-index)一定要注意):

.camera {position: relative;width: 100%;height: 100%;.shade {position: absolute;top: 0;left: 0;box-sizing: border-box;width: 100%;height: 100%;z-index: 10;}.video {position: absolute;top: 0;left: 0;height: 100%;width: 100%;z-index: 5;background: #000;}.shootBtn {position: absolute;top: 0.4rem;left: 0;z-index: 15;height: 0.6rem;padding-right: 0.2rem;background: rgba(255, 255, 255, 0.3);border-top-right-radius: 0.1rem;border-bottom-right-radius: 0.1rem;font-size: 0.32rem;color: #fff;text-align: center;display: flex;align-item: center;   }
}

3. JS:

import Camera from './camera.js';data() {return {camera: null,isScan: false,isDrawCanvas: false,takePictureInterval: null,bankNumber: '',}
},methods: {/*** 点击扫描按钮触发函数,调用摄像头插件开启摄像头*/scanBank() {this.isScan = !this.isScan // 显示银行卡识别的界面this.$nextTick(() => { // 渲染完成后执行if(this.isScan && !this.isDrawCanvas) {this.drawBankMask() // 如果没有画遮罩,将遮罩画出来}let canvasMask = document.getElementById("canvasMaskShade")let self = this// 调用插件开启摄像头,两个参数,第一个参数是video数据,第二个是遮罩层数据,用于视频剪切self.camera = Camera.create({video: document.querySelector('#video'),width: canvasMask.clientWidth,height: canvasMask.clientHeight,onError: function(error) {console.log("调用扫描失败")self.isScan = !self.isScan //调用失败后关闭识别页面}},{canvasStyle: {"width": canvasMask.bankWidth,"height": canvasMask.bankHeight,"marginTop": canvasMask.bankMarginTop,"marginLeft": canvasMask.bankMarginLeft}})})// 设置定时器,定时将此刻获取的图片传输到后台进行识别银行卡来完成自动识别功能,// 定时器将一直执行,直到用户取消识别或银行卡识别成功后,将定时器清除;// 如果调用摄像头失败或开始识别失败,则会清除定时器clearInterval(this.takePictureInterval)this.takePictureInterval = setInterval(() => {this.takePicture()}, 3000)},/*** 绘制遮罩层*/drawBankMask() {let canvasMask = document.getElementById('canvasMaskShade')let screenWidth = canvasMask.clientWidth // 获得可视区域宽度let screenHeight = canvasMask.clientHeight // 获得可视区域高度canvasMask.width = screenWidth // 设置遮罩的width属性canvasMask.height = screenHeight // 设置遮罩的height属性let bankWidth = screenWidth - 40 // 设置银行卡框的宽度,规定距离左右屏幕边缘20pxlet bankHeight = Math.ceil(bankWidth / 1.6) // 根据银行卡宽高比设置银行卡框的高度,取整数let otherHeight = (screenHeight - bankHeight) / 2 // 设置银行卡框在屏幕中间位置,故这样计算其距离屏幕顶部的高度let bankMarginTop = Math.floor(otherHeight) // 设置银行卡框距离顶部距离,取整数canvasMask.bankMarginTop = bankMarginTop // 添加银行卡框距离屏幕上下两侧距离属性,用作camera插件参数:将视频流转换成canvas时的画布剪切y坐标计算参数canvasMask.bankMarginLeft = 20 // 添加银行卡框距离屏幕左右两侧距离属性,用作camera插件参数:将视频流转换成canvas时的画布剪切x坐标计算参数canvasMask.bankWidth = bankWidth // 添加银行卡框宽度属性,用作camera插件参数:将视频流转换成canvas时画布剪切宽度的计算参数canvasMask.bankHeight = bankHeight // 添加银行卡框高度属性,用作camera插件参数:将视频流转换成canvas时画布剪切高度的计算参数// 开始画遮罩层let ctx = canvasMask.getContext('2d');ctx.fillStyle = "rgba(51, 51, 51, 0.5)";ctx.fillRect(0, 0, screenWidth, screenHeight)// 银行卡框边框常量,画一个银行卡边框,将银行卡框凸显出来const lineWidth = 4const lineColor = "#DCDCDC"// 银行卡框上边框绘制ctx.beginPath();ctx.moveTo(17, bankMarginTop - 2);ctx.lineTo(bankWidth + 23, bankMarginTop - 2);ctx.closePath();ctx.lineWidth = lineWidth;ctx.strokeStyle = lineColor;ctx.stroke();// 银行卡框左边框绘制ctx.beginPath();ctx.moveTo(18, bankMarginTop - 3);ctx.lineTo(18, bankMarginTop + bankHeight + 3);ctx.closePath();ctx.lineWidth = lineWidth;ctx.strokeStyle = lineColor;ctx.stroke();// 银行卡框下边框绘制ctx.beginPath();ctx.moveTo(17, bankMarginTop + bankHeight + 2);ctx.lineTo(bankWidth + 23, bankMarginTop + bankHeight + 2);ctx.closePath();ctx.lineWidth = lineWidth;ctx.strokeStyle = lineColor;ctx.stroke();// 银行卡框右边框绘制ctx.beginPath();ctx.moveTo(bankWidth + 22, bankMarginTop - 3);ctx.lineTo(bankWidth + 22, bankMarginTop + bankHeight + 3);ctx.closePath();ctx.lineWidth = lineWidth;ctx.strokeStyle = lineColor;ctx.stroke();ctx.clearRect(20, bankMarginTop, bankWidth, bankHeight) // 清除银行卡框的遮罩,展示出银行卡应放的位置this.isDrawCanvas = true},/*** 将此刻得到的图片传输到后台进行银行卡验证*/takePicture() {let self = thisif(!self.isScan) { // 如果没有展示扫描页面或者调用扫描失败,则清除定时任务clearInterval(self.takePictureInterval)return}self.camera.snapshot(function(imageUrl) {let imageBox = document.querySelector('#imageBox');let image = imageBox.querySelector('img');if (!image) {image = document.createElement('img');image.src = imageUrl;document.querySelector('#imageBox').appendChild(image)}else {image.src = imageUrl;}// 请求接口识别let params = { // 接口参数data: image.src,type: 'jpeg'}this.axios.post(...).then( res => {// 如果请求成功,将定时器清除,关闭摄像头,关闭银行卡识别界面self.bankNumber = res.data.number // 将识别的银行卡号码赋值,假设识别出来的银行卡信息存放在data的number中clearInterval(self.takePictureInterval);self.camera.puseself.$nextTick(() => {self.isScan = false})})})},/*** 手动取消银行卡识别*/cancelScan() {this.camera.pause() // 关闭摄像头clearInterval(this.takePictureInterval) // 清除定时器this.isScan = false // 关闭银行卡识别页面},
}

银行卡识别效果图:

银行卡识别界面

完。

Vue (用javaScript/JS)调用媒体摄像头拍照扫描银行卡相关推荐

  1. 摄像头网页服务器,js调用本地摄像头拍照并上传到web服务器

    [实例简介] js调用本地摄像头拍照并上传到web服务器.后台使用java实现图片的接收和存储,上传的图片默认保存到项目下的images文件夹中. [实例截图] [核心代码] MyCamera └── ...

  2. JS调用本地摄像头拍照(兼容各大浏览器及IE8+)

    最近做的项目遇到了个难题,使用video+canvas+getUserMedia()写的调用本地摄像头拍照不兼容IE. 原因:IE8及以下不支持HTML5标签:video和canvas:IE11及以下 ...

  3. html5 调用摄像头 支持IE,JS调用本地摄像头拍照(兼容各大浏览器及IE8+)

    最近做的项目遇到了个难题,使用video+canvas+getUserMedia()写的调用本地摄像头拍照不兼容IE. 原因:IE8及以下不支持HTML5标签:video和canvas:IE11及以下 ...

  4. js调用本地摄像头拍照截图,提交后台

    今天有个需求,需要在前端界面调用本地摄像头,然后拍照结束后可以截取预览,最后将结果提交到后台.查了网上很多的插件,发现适合的非常少,于是决定自己修改一个. 这里我修改了一个jquery插件,把摄像头拍 ...

  5. js调用pc摄像头实现拍照、录视频等,新版Chrome无访问http页面无法打开麦克风、摄像头

    js调用pc摄像头实现拍照.录视频等,新版Chrome无访问http页面无法打开麦克风.摄像头 新版Chrome配置 vue环境下的前端 function部分 ##由于没有https环境,只有http ...

  6. 使用js调用设备摄像头并实现拍照

    使用getUserMedia这个API来获取摄像头的权限并实现拍照 在线体验:https://811w1z2xwj.codesandbox.io/ 下面是源码: <!DOCTYPE html&g ...

  7. 原生js 调用电脑摄像头完成拍照

    原生js 调用电脑摄像头完成拍照 人脸登录新发版1.0x 免费开源,保姆级别教程人脸登录地址 1 ,完成拍照后可转换成base64码,你可以对当前base64码进行操作,当初我就利用这个功能点完成前端 ...

  8. H5调用手机摄像头拍照,如何压缩后上传

    H5调用手机摄像头拍照后,怎样压缩再上传? 实际的压缩功能,就是利用canvas画布功能,将图片进行裁剪后保存图片的base64数据流,然后上传. 案例全部代码,示下: <!DOCTYPE ht ...

  9. java 调用手机相册_微信公众号调用手机摄像头拍照和本地相册

    [实例简介] 微信公众号调用手机摄像头拍照和本地相册,上传服务器 [实例截图] [核心代码] 微信公众号调用手机摄像头拍照和本地相册(2) └── 微信公众号调用手机摄像头拍照和本地相册 └── 微信 ...

最新文章

  1. 即日起更新机器学习相关博客
  2. 《陶哲轩实分析》部分勘误
  3. android开发使用c+_如何在Android项目中开始使用C ++代码
  4. python学习day32 黏包 struct模块
  5. 【java】【乱码】Java 读取本地 UTF8 txt文件乱码处理
  6. Python学习笔记之几点代码格式要求
  7. WatiN-Html元素及元素属性识别-扩展
  8. np.array的shape的区别
  9. 如何彻底杀掉nginx进程
  10. python动态导入类或函数_Python 动态从文件中导入类或函数的方法
  11. 论坛网站进行帖子保存php,PHPwind论坛专用采集器
  12. 031--python--打印机票页面
  13. PP实施经验分享(24)——ECN应用及系统操作
  14. 3GPP TS 29244-g30 中英文对照 | 5.11 User plane inactivity detection and reporting
  15. Spring的四种注入方式
  16. KITTI如何submit自己的模型效果
  17. (JAVA编成练习):递归的使用,简单的列子帮你理解递归。
  18. fc安卓模拟器_面对悠长假期,GPD WIN2掌机让我畅玩模拟器游戏
  19. ALV导出到EXCEL数据被截断
  20. java jar反编译后保存_java根据jar包反编译后修改再打包回jar的做法

热门文章

  1. Android动画——使用动画启动Activity
  2. MS Access 数据库操作使用OledbParameter出现的怪异问题
  3. oracle如何开启归档模式
  4. vue使用backgroundImage属性
  5. Windows批处理:命令if
  6. 如何查看Nginx日志中关于百度爬虫的日志记录
  7. 国内常见的路由器默认帐户及密码
  8. Nodejs 如何发送邮件(Gmail 和 126邮箱)
  9. 【vue】vuex中modules的基本用法
  10. SQL语句中EXISTS的用法