/** * 组件: 调用摄像头拍摄的构造函数 * @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方法包裹一个Promise return 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 // 旧的浏览器可能没有srcObject if ("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) }) } }, // 将录像绘制到canvas drawVideo: 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 = 1 if (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 * 1) // 计算剪切视频的x坐标 let y = Math.floor((this.shadeMarginTop - videoMarginTop) * 1) // 计算剪切视频的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 = this let 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