基本原理就是通过浏览器相关api打开本地摄像头,然后把视频流传给video标签播放出来,通过canvas进行图片截取,不断生成图片,进行人脸比对。
1、调取本地摄像头
<!--展示摄像头视频流-->
<video ref="video" width="462" height="462" autoplay></video>
<!-- canvas截取图片 -->
<canvas class="canv" ref="canvas" width="400" height="300"></canvas>
// 调用摄像头
callCamera() {
// H5调用电脑摄像头API
let videoObj = { video: true };
var errBack = function (error) {
console.log("Video capture error: ", error.code);
};
// 这个是最新版浏览器获取视频流的方法
// 如果不需要兼容旧浏览器只写这一个方法即可
if (navigator.mediaDevices.getUserMedia) { // Standard
navigator.mediaDevices.getUserMedia(videoObj).then(stream => {
this.$refs["video"].srcObject = stream;
this.$refs["video"].play();
this.info = '请调整坐姿,对准摄像头!'
console.log('摄像头开启');
// 开始拍照
this.startCap();
});
} else if (navigator.getUserMedia) { // WebKit-prefixed
navigator.webkitGetUserMedia(videoObj, function (stream) {
this.$refs["video"].src = window.URL.createObjectURL(stream);
this.$refs["video"].play();
}, errBack);
} else if (navigator.webkitGetUserMedia) { // WebKit-prefixed
navigator.webkitGetUserMedia(videoObj, function (stream) {
this.$refs["video"].src = window.URL.createObjectURL(stream);
this.$refs["video"].play();
}, errBack);
}
else if (navigator.mozGetUserMedia) { // Firefox-prefixed
navigator.mozGetUserMedia(videoObj, function (stream) {
this.$refs["video"].mozSrcObject = stream;
this.$refs["video"].play();
}, errBack);
}
},
// 开始拍照
// 注意 这里使用了lodash的debounce方法(需要自行安装 lodash)
startCap: _.debounce(function () {
console.log('this', this);
let ctx = this.$refs["canvas"].getContext("2d");
// 把当前视频帧内容渲染到canvas上
ctx.drawImage(this.$refs["video"], 0, 0, 400, 300);
// 转base64格式、图片格式转换、图片质量压缩
let imgBase64 = this.$refs["canvas"].toDataURL("image/jpeg", 0.7);
// console.log('img --- ', imgBase64);
// let str = imgBase64.replace("data:image/jpeg;base64,", "");
var blob = this.dataURLtoBlob(imgBase64);
var file = this.blobToFile(blob, Date.now());
this.handleLogInByFace(file)
}, 800),
2、canvas截取图片 转换位file文件
// canvas截取到的图片是base64的,如果后台对比支持base64格式的 直接传至后台即可
// 如果后台不支持base64格式的 需要将base64格式的转化位File格式,方法如下:
// 这两个方法已经在上边的startcap方法中使用到了,目的就是转换为file文件的
// 将base64转换为blob
dataURLtoBlob (dataurl) {
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);
}
return new Blob([u8arr], { type: mime });
},
//将blob转换为file
blobToFile (theBlob, fileName){
theBlob.lastModifiedDate = new Date();
theBlob.name = fileName;
return theBlob;
},
3、拿到的图片请求接口 进行比对
// 这里就是开始请求后台接口了
// 需要注意的是 这个拍照是不断在拍的 不断的请求后台 只要比对失败 就继续拍照 直至成功
// 调用接口传图片请求
async handleLogInByFace (file) {
try {
let id = 99;
var formdata=new FormData();
formdata.append("file", file);
formdata.append("deptId", id);
let res = await doLoginByFace(formdata);
console.log('人脸请求', res);
let {
code,
msg,
data
} = res.data;
if (code == 200) {
this.info = msg;
// 注意成功的时候 一定要结束占用摄像头
this.closeCamera();
sessionStorage.setItem('token', data);
this.$router.replace('/home').catch({});
} else if (code == -2) {
this.$message({
type: 'error',
message: `${msg}`
})
this.$store.commit('CLEAR_LOGIN');
this.$router.push('/login').catch({});
} else {
this.info = msg;
var that = this;
this.startCap();
}
} catch(e) {
console.log('requestOutStore',e)
}
},
// 关闭摄像头
closeCamera() {
if (!this.$refs["video"].srcObject) {
this.dialogCamera = false;
return;
}
let stream = this.$refs["video"].srcObject;
let tracks = stream.getTracks();
tracks.forEach((track) => {
track.stop();
});
this.$refs["video"].srcObject = null;
this.clearInterval();
},
最后,注意在页面销毁的时候,为了安全起见,还是再一次清除摄像头的占用。