前端Js自定义相机取景框

您所在的位置:网站首页 标签云标签加边框js 前端Js自定义相机取景框

前端Js自定义相机取景框

2024-07-10 08:07| 来源: 网络整理| 查看: 265

参考文章:

实现的demo:https://www.freesion.com/article/67641324321/ getUserMedia:https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia 一些问题合集:https://www.jb51.net/html5/722394.html 无法使用getUserMedia的原因:https://stackoverflow.com/questions/58808091/how-can-i-replace-the-getusermedia-function-in-mozilla 效果展示

2NdSIS.gif

html部分

主要分为四部分

启动按钮 原始相机 自定义相机(重点) 上传提示 开启人脸采集 照片 取消 重拍 使用照片 采集成功 恭喜您,完成人脸照片采集 {{'返回'+btntext}} 采集失败 {{tipInfo.msg+',请重拍'}} {{'返回'+btntext}} js部分

变量部分:

data() { return { type:'',//上传类型 update|upload cameraShow:false,//启动自定义相机 status:0,//自定义相机-拍摄进度:0|未开启 1|开启但未拍摄 2|开启且已拍摄 imageUrl:'',//自定义相机-抓拍url front:true,// 自定义相机-前置与后置转换(未验证) //提示部分 tipVisible:false, tipInfo:{ result:'fail', msg:'采集人脸失败' },//上传结果 btntext:'',//倒计时文本 time:null,//计时器 imageFile:'',//图片对象 }; }, 启动相机

openCamera:主要做了一些兼容与回退。

openCamera() { // 1. 先展示,因为要从这里获取video标签 this.cameraShow=true // 2. constraints:指定请求的媒体类型和相对应的参数 var constraints={ audio: false, video: { facingMode: (this.front? "user" : "environment") } } // 3. 兼容部分: // 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象 if (navigator.mediaDevices === undefined) { navigator.mediaDevices = {}; } // 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia // 因为这样可能会覆盖已有的属性。这里我们只会在没有getUserMedia属性的时候添加它。 if (navigator.mediaDevices.getUserMedia === undefined) { navigator.mediaDevices.getUserMedia = function(constraints) { // 首先,如果有getUserMedia的话,就获得它 var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia; // 一些浏览器根本没实现它 - 那么就返回一个error到promise的reject来保持一个统一的接口 if (!getUserMedia) { return Promise.reject(new Error('getUserMedia is not implemented in this browser')); } // 否则,为老的navigator.getUserMedia方法包裹一个Promise return new Promise(function(resolve, reject) { getUserMedia.call(navigator, constraints, resolve, reject); }); } } // 4. 获取视频流 let that=this navigator.mediaDevices.getUserMedia(constraints) .then(function(stream) { // 进来这里表示能够兼容 let video=document.querySelector('video'); video.srcObject = stream; video.onloadedmetadata = function(e) { video.play(); }; // 进入自定义拍摄模式 that.status=1 }) .catch(function(err) { // 进来这里表示不能兼容 console.log('nonono',err) // 调用原始摄像头 that.originCamera() }); }, 方案一:兼容

snapPhoto:抓拍

snapPhoto(){ var canvas = document.querySelector('#mycanvas'); var video = document.querySelector('video'); canvas.width = video.videoWidth; canvas.height = video.videoHeight; canvas.getContext('2d').drawImage(video,0,0); // 保存为文件,用于后续的上传至服务器(尚未实践)——>后续提交 this.imageFile=this.canvasToFile(canvas) // blob转url:用于展示 let p=new Promise((resolve,reject)=>{ canvas.toBlob(blob=>{ let url=URL.createObjectURL(blob) resolve(url) }); }) let that=this p.then(value=>{ that.imageUrl=value that.status=2//表示拍摄完成 }) },

canvasToFile:canvas转为文件格式,用于上传服务器

canvasToFile(canvas){ var dataurl = canvas.toDataURL("image/png"); var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } var file = new File([u8arr], "phone.png", {type: mime}); return file }, 方案二:不兼容

originCamera:调用原始摄像机

originCamera(){ let that=this //关闭自定义相机 that.cameraShow=false let promise= new Promise(function (resolve, reject) { let file=document.getElementById('file') file.click() file.onchange = function (event) { if (!event) { reject('empty') } //当选中或者拍摄后确定图片后,保存图片文件——>后续提交 let file=event.target.files[0] resolve(file) } }) promise.then((value)=> { that.submitPhoto('origin',value) } ) }, 提交与上传提示 submitPhoto(type,file) { if(type=='origin'){ this.imageFile=file } console.log("提交",this.imageFile); //在这里进行上传操作 // let fd=new FormData() // fd.append("face_image",this.imageFile) //... //上传成功时: this.tipInfo.result='ok' // this.tipInfo.result='fail' this.cameraShow=false //开始提示 this.countdown() }, //倒计时与提示 countdown(){ clearInterval(this.time); this.tipVisible=true let coden = 3; this.btntext = '('+coden+'s)'; this.time = setInterval(() => { coden-- this.btntext = '('+coden+'s)'; if (coden == 0) { clearInterval(this.time); this.tipVisible = false; this.btntext = ""; } }, 1000); }, css部分 .uploadFacePic{ .img{ width:100%; height:100% } .bigger{ font-weight: 600; font-size: 3em; } .small{ font-size: 2em; } .smaller{ font-size: 1.2em; } // 控制台 .control{ width: 100%; position: fixed; left: 0; bottom: 0; top: 75vh; right: 0; background:black; .control_before{ position:relative; width:100%; height:100%; display: flex; flex-direction: column; .control_before_top{ z-index: 1; flex: 1; color: orange; display: flex; justify-content: center; align-items: center; } .control_before_bottom{ flex: 2; display: flex; justify-content: space-around; color: white; align-items: center; margin-bottom: 1.5em; } } .control_after{ position:relative; width:100%; height:100%; display: flex; color: white; align-items: center; justify-content: space-around; } } main{ text-align: center; } .tipinfo{ z-index: 2; position: fixed; top:46px; left: 0; right: 0; bottom: 0; background: white; display: flex; justify-content: center; align-items: center; .successContent{ .van-icon{ color: #f68618; font-size: 5em; } .title{ color: #f68618; font-size: 1.8em; } } .failContent{ .van-icon{ color: red; font-size: 5em; } .title{ color: red; font-size: 1.8em; } } .info{ margin: 1em 0 3em; } } .btn{ height: 34px; width: 80vw; background: #f68618; color: white; display: flex; justify-content: center; align-items: center; border-radius: .3em; margin: 0 auto; } } 后记 部分icon是从阿里导入的,这里没有写出来。 目前在微信开发者工具能够看到自定义相机的效果,在真机上(http)还启动不了,查阅资料说navigator.mediaDevices.getUserMedia服务器需要用https(尚未验证) 自定义相机自拍时会出现镜像效果,暂时不知道怎么处理。 中间上传时估计要做一个加载ui进行过渡 优化:video自适应大小

在html中,屏幕中显示的可见视图大小其实我们一开始就规定好了,但是不同设备有它的大小,那怎么统一且自适应呢?

video标签原始的object-fit的属性值为contain,这就造成了问题:没有办法填充我们定义的可见视图大小——解决:object-fit:cover

然额还没结束。了解object-fit:cover的原理后,canvas也应该对应变化。cover的情况下,当宽高不一致时,超出部分会被剪切。此外,cover会自适应,即它不会只从一边裁剪,而是端水从两边等份裁剪。

var canvas = document.querySelector('#mycanvas'); var video = document.querySelector('video'); // 基于object-fit:cover // 原始宽高 let width=video.videoWidth let height=video.videoHeight //判断宽高是否一致 const WIDTH_UNEQUAL_HEIGHT=width!=height //被裁减掉的一边的大小 const SPAN=Math.abs(width-height)/2 //根据差值定义裁剪起点x,y。 //例:如果高比宽高出10,那么上面会被裁减掉5,那么起点y就等于5,这样就保证了裁剪到的是可视图片 let cut_x=WIDTH_UNEQUAL_HEIGHT?SPAN:0 //裁剪起点x let cut_y=WIDTH_UNEQUAL_HEIGHT?0:SPAN //裁剪起点y //如果裁剪过,宽度要把裁剪部分去掉。 let cut_after_width=cut_x!=0?width-2*SPAN:width let cut_after_height=cut_y!=0?height-2*SPAN:height //绘制画布(裁剪后的) canvas.width = cut_after_width; canvas.height = cut_after_height; canvas.getContext('2d').drawImage(video,cut_x,cut_y,cut_after_width,cut_after_height,0,0,cut_after_width,cut_after_height)

ok完成。

优化:video镜像翻转

受前面的启发。

稍微改写一下画布

let context=canvas.getContext('2d') context.drawImage(video,cut_x,cut_y,cut_after_width,cut_after_height,0,0,cut_after_width,cut_after_height) // 如果是前置 需要镜像翻转 if(this.front){ context.scale(-1, 1); context.drawImage(video,cut_x,cut_y, width*-1,height); }

但是查到资料说ios的scale无法接收负数。。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3