浏览器调起摄像头(jquery+layui)
/* 实例化camvas配置参数 config = { video:{width:Number(scale*4),height:Number(scale*3)},//视频比例4:3 canvasId:'canvas',//画布canvas节点ID videoId:'v',//video节点ID imgType:'png',//图片类型,/png|jpeg|bmp|gif/ quality:'1' //图片质量0-1之间 } */ window.URL = window.URL || window.webkitURL||window.mozURL || window.msURL; navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame // Integrate navigator.getUserMedia & navigator.mediaDevices.getUserMedia function getUserMedia (constraints, successCallback, errorCallback) { if (!constraints || !successCallback || !errorCallback) {return} if (navigator.mediaDevices) { navigator.mediaDevices.getUserMedia(constraints).then(successCallback, errorCallback) } else { navigator.getUserMedia(constraints, successCallback, errorCallback) } } //获取摄像头设备源 function getMediaStream() { var exArray = []; //存储设备源ID MediaStreamTrack.getSources(function (sourceInfos) { for (var i = 0; i != sourceInfos.length; ++i) { var sourceInfo = sourceInfos[i]; //这里会遍历audio,video,所以要加以区分 if (sourceInfo.kind === 'video') { exArray.push(sourceInfo.id); } } }); return exArray; } //用户手机端使用后置摄像头 function getMediaConfig() { if (navigator.getUserMedia) { if(/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)){ //手机端 return { 'video': {'optional': [ {'sourceId': getMediaStream()[1] //0为前置摄像头,1为后置 }] }, 'audio':false } }else{ return {'video':true,'audio':false} } } else { alert('Native device media streaming (getUserMedia) not supported in this browser.'); } } // The function takes a canvas context and a `drawFunc` function. // `drawFunc` receives two parameters, the video and the time since // the last time it was called. function camvas(config) { var self = this self.convas = document.getElementById(config.canvasId) self.ctx = self.convas.getContext('2d'); self.config = config self.isStop = false; //video节点ID self.video = document.getElementById(self.config.videoId) //video 显示尺寸 self.video.setAttribute('width', this.config.video.width) self.video.setAttribute('height', this.config.video.height) //视频流控制句柄 var mediaStreamTrack; //对外开启视频方法 this.startCamera = function () { // The callback happens when we are starting to stream the video. getUserMedia(getMediaConfig(), function(stream) { // Yay, now our webcam input is treated as a normal video and // we can start having fun try { mediaStreamTrack = typeof stream.stop === 'function' ? stream : stream.getTracks().length==1 ? stream.getTracks()[0]:stream.getTracks()[1]; if(self.video.mozSrcObject !== undefined){ //Firefox中,video.mozSrcObject最初为null,而不是未定义的,我们可以靠这个来检测Firefox的支持 self.video.mozSrcObject = stream; }else{ self.video.srcObject = stream; } } catch (error) { self.video.src = window.URL && window.URL.createObjectURL(stream) || stream; } self.isStop = false; self.video.play(); // Let's start drawing the canvas! // self.recordVideo() }, function(err){ alert(err); }) } //录像方法 this.recordVideo = function() { var self = this var last = Date.now() var loop = function() { // For some effects, you might want to know how much time is passed // since the last frame; that's why we pass along a Delta time `dt` // variable (expressed in milliseconds) var dt = Date.now() - last self.draw(self.video, dt) last = Date.now() requestAnimationFrame(loop) } requestAnimationFrame(loop) } //停止视频 this.stop = function () { self.ctx.clearRect(0, 0, self.config.video.width,self.config.video.height); mediaStreamTrack && mediaStreamTrack.stop(); self.isStop = true; } //拍照,base64/image/png this.drawImage=function (callback) { if(!self.isStop){ self.ctx.drawImage(self.video,0,0,self.config.video.width,self.config.video.height); var base64URL = self.convas.toDataURL('image/'+self.config.imgType,self.config.quality); callback&&callback(base64URL); } } //录像数据帧 this.draw = function(video, dt) { self.ctx.drawImage(video, 0, 0) } }
<style> .camera-control { position: absolute; z-index: 10; left: 0; right: 0; bottom: 0; text-align: center; padding: 10px; min-height: 40px; } .camera-btn { cursor: pointer; border: none; color: #fff; padding: 8px 12px; font-size: 14px; outline: none; background-color: rgba(255, 255, 255, 0.3); transition: all 0.3s; box-shadow: 0 2px 0 0 rgba(45, 140, 240, 0.8); margin: 0 5px; } .camera-canvas-group { display: flex; left: 0; right: 0; height: 80px; } .camera-canvas-item { opacity: 0.8; flex: 1; max-width: 100px; height: 100%; overflow: hidden; clear: both; margin-bottom: -1px; transition: all 0.2s; position: relative; } .camera-canvas-item img { float: left; width: 100%; background-color: #000; border: 1px solid #fff; } .camera-canvas-item:hover { position: relative; opacity: 1; box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.8); } .camera-canvas-item:hover {} .camera-btn:hover { background-color: rgba(45, 140, 240, 0.6); box-shadow: 0 2px 0 0 rgba(45, 140, 240, 1); } .camera-btn:active { background-color: rgba(45, 140, 240, 0.2); } .canvas-item-del{ position: absolute; right: 0px; font-size: 22px; color: #ef475d; cursor: pointer; } .camera-show-pc{ background-color:#FFFFFF;margin:0px auto; } </style> <div class="layui-fluid layui-anim" id="camera" lay-title="摄像头测试"> <div class="layui-row"> <div class="layui-card" style="background-color:#1E8AE8;margin: auto auto;"> <div class="layui-card-body"> <div class="layui-row camera-show-pc"> <h1 style="padding: 23px 0px;;font-size: 2.4rem;color: #1E8AE8;font-weight: bold;text-align: center">camera</h1> <div class="bag_display_flex" style="justify-content: center;"> <div style="position: relative;"> <video id="v" style="background-color: #000000"></video> <div class="camera-control"> <button type="button" id="stop" class="layui-icon layui-icon-stop camera-btn">关闭</button> <button type="button" id="start" class="layui-icon layui-icon-triangle-r camera-btn">开始</button> <button type="button" id="snap" class="layui-icon layui-icon-camera-fill camera-btn">拍照</button> <button type="button" id="save" class="layui-icon layui-icon-save camera-btn">保存</button> <button type="button" id="clear" class="layui-icon layui-icon-fonts-clear camera-btn">清空</button> </div> </div> <div> <canvas id="canvas" style="margin-left: 2px;background-color: #000000"></canvas> </div> </div> <div class="layui-row" style="position: relative"> <div class="camera-canvas-group"></div> </div> </div> </div> </div> </div> </div> <script type="text/javascript" src="js/camera/camvas.js"></script> <script data-th-inline="javascript" type="text/javascript"> layui.use(['jquery'], function () { var $ = layui.jquery, $view = $('#camera'), config={}, myCamvas; init(); onClick(); function init() { let scale = 120;//宽高比例倍数 config = { video:{width:Number(scale*4),height:Number(scale*3)},//4:3 canvasId:'canvas', videoId:'v', imgType:'png', quality:'1' //图片质量0-1之间 }; $('.camera-show-pc').css('width',config.video.width*2+20); $view.find('#canvas').attr('width',config.video.width); $view.find('#canvas').attr('height',config.video.height); //拍照实例化 myCamvas = new camvas(config); myCamvas.startCamera(); $view.find('#start').hide(); } function onClick() { //停止拍照 $view.find('#stop').click(function () { myCamvas.stop() $view.find('#stop').hide() $view.find('#start').show() }) //启动摄像头 $view.find('#start').click(function () { myCamvas.startCamera(); $view.find('#stop').show() $view.find('#start').hide() }) //拍照 $view.find('#snap').click(function () { myCamvas.drawImage(function drawImage(base64URL) { let xsCamera = $('<div></div>').addClass('camera-canvas-item'); xsCamera.append($('<img>').attr('src',base64URL)).append($('<span></span>').addClass('layui-icon layui-icon-close-circle-fill canvas-item-del')); $('.camera-canvas-group').prepend(xsCamera); }); }) //清空 $view.find('#clear').click(function () { $view.find('.camera-canvas-group').empty() }) //append方式添加节点直接click无效,点击显示大图 $view.find('.camera-canvas-group').on('click','.camera-canvas-item img',function () { myCamvas.ctx.drawImage(this,0,0,config.video.width,config.video.height) }) //删除图片 $view.find('.camera-canvas-group').on('click','.camera-canvas-item .canvas-item-del',function () { //删除父节点元素 $(this).closest('.camera-canvas-item').remove() }) //保存所有图片到本地 $view.find('#save').click(function () { let fileName = getFileName(); let el_a = $('<a>'); layui.each($view.find('.camera-canvas-group .camera-canvas-item'),function (k,item) { let t_filename = fileName+'_'+k+'.'+config.imgType; downLoad($(this).find('img').attr('src'),t_filename,config.imgType); }) }) //空格按下拍照 $(document).keypress(function (e) { e.keyCode===32&&$('#snap').trigger('click'); }) }; //tode function getFileName() { let date = new Date(); let fileName = ''+date.getFullYear()+(date.getMonth()<9?+'0':'') +(date.getMonth()+1) +(date.getDate()<10?+'0':'')+date.getDate() +date.getHours() +date.getMinutes()+(date.getSeconds()<10?'0':'')+date.getSeconds(); return fileName; } function downLoad(dataURL,fileName,fileType) { var reader = new FileReader(); reader.readAsDataURL(createFile(dataURL)); reader.onload = function (e) { if ('msSaveOrOpenBlob' in navigator) { // IE,Edge var base64file = e.target.result + ''; window.navigator.msSaveOrOpenBlob(createFile(base64file.replace('data:' + fileType + ';base64,', ''), fileType), fileName); } else { // chrome,firefox var link = document.createElement('a'); link.style.display = 'none'; link.href = e.target.result; link.setAttribute('download', fileName); // document.body.appendChild(link); link.click(); $(link).remove(); } } /*dataURL = dataURL.replace('image/'+fileType,'image/octet-stream'); var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a'); save_link.href = dataURL; save_link.download = fileName; $(save_link).click() var event = document.createEvent('MouseEvents'); event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); save_link.dispatchEvent(event);*/ }; // 解析 BASE64文件内容 for IE,Edge function createFile(urlData) { var arr = urlData.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = window.atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new Blob([u8arr], { type: mime }); } }); </script>