253 lines
8.1 KiB
HTML
253 lines
8.1 KiB
HTML
<!doctype html>
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>人脸识别</title>
|
||
<script src="./js/vue.js"></script>
|
||
<script type="text/javascript" src="./js/jquery-3.3.1.min.js"></script>
|
||
<script src="../config.js"></script>
|
||
<script src="build/tracking-min.js"></script>
|
||
<script src="build/data/face-min.js"></script>
|
||
|
||
<style>
|
||
#video {
|
||
transform: rotateY(180deg);
|
||
}
|
||
.face-capture {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.face-capture video, .face-capture canvas {
|
||
position: fixed;
|
||
top: 0;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
z-index: 2;
|
||
background-repeat: no-repeat;
|
||
background-size: 100% 100%;
|
||
}
|
||
|
||
.face-capture canvas {
|
||
z-index: 2;
|
||
}
|
||
|
||
.face-capture .img-cover {
|
||
position: fixed;
|
||
top: 0;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
z-index: 2;
|
||
background-repeat: no-repeat;
|
||
background-size: 100% 100%;
|
||
}
|
||
|
||
.face-capture .rect {
|
||
border: 2px solid #0aeb08;
|
||
position: fixed;
|
||
z-index: 3;
|
||
}
|
||
|
||
.face-capture .control-container {
|
||
margin-top: 10rem;
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
z-index: 4;
|
||
background-repeat: no-repeat;
|
||
background-size: 100% 100%;
|
||
}
|
||
|
||
.face-capture .title {
|
||
text-align: center;
|
||
color: white;
|
||
margin: 1.6rem auto;
|
||
font-size: 28px;
|
||
}
|
||
|
||
.face-capture .close {
|
||
width: 0.8rem;
|
||
height: 0.8rem;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="app">
|
||
<div v-show="showContainer" class="face-capture" id="face-capture">
|
||
<video ref="refVideo" id="video" autoplay></video>
|
||
<!-- <img src="./../images/video-cover.png" alt="cover" class="img-cover"/> -->
|
||
<div class="control-container face-capture">
|
||
<h2 class="title">{{scanTip}}</h2>
|
||
<h2 class="title">{{msg}}</h2>
|
||
<!-- <img class="close" src="./../images/address_edit_clear.png" alt=""/> -->
|
||
<canvas ref="refCanvas" :width="screenSize.width" :height="screenSize.height" :style="{opacity: 0}"></canvas>
|
||
</div>
|
||
<div class="rect" v-for="item in profile"
|
||
:style="{ width: item.width + 'px', height: item.height + 'px', left: item.left + 'px', top: item.top + 'px'}"></div>
|
||
</div>
|
||
<div v-show="!showContainer">验证成功</div>
|
||
</div>
|
||
</body>
|
||
</html>
|
||
<script type="text/javascript">
|
||
var vm = new Vue({
|
||
el: '#app',
|
||
|
||
data: {
|
||
USER_ID: null,
|
||
screenSize: { width: window.screen.width, height: window.screen.height },
|
||
URL: null,
|
||
streamIns: null, // 视频流
|
||
showContainer: true, // 显示
|
||
tracker: null,
|
||
tipFlag: false, // 提示用户已经检测到
|
||
flag: false, // 判断是否已经拍照
|
||
context: null, // canvas上下文
|
||
profile: [], // 轮廓
|
||
removePhotoID: null, // 停止转换图片
|
||
scanTip: '人脸识别中...请将人脸置于镜头内', // 提示文字
|
||
imgUrl: '', // base64格式图片
|
||
weburl: config.weburl,
|
||
msg: '',
|
||
countdown: 0,
|
||
timer: null
|
||
},
|
||
beforeDestroy() {
|
||
if (this.timer) {
|
||
clearInterval(this.timer)
|
||
this.timer = null
|
||
}
|
||
},
|
||
mounted() {
|
||
this.USER_ID = this.getUrlKey('USER_ID')
|
||
// 这段代 主要是获取摄像头的视频流并显示在Video 签中
|
||
window.addEventListener('DOMContentLoaded', function() {
|
||
// 拍照按钮
|
||
// $("#snap").click(function () {
|
||
// context.drawImage(video, 0, 0, 330, 250);
|
||
// })
|
||
// 拍照每秒一次
|
||
// setInterval(function(){
|
||
// context.drawImage(video, 0, 0, 330, 250)
|
||
// },1000);
|
||
var constraints = { 'audio': false, 'video': { 'facingMode': 'user' }}
|
||
|
||
// 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象
|
||
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
|
||
// 一些浏览器根本没实现它 - 那么就返回一个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)
|
||
})
|
||
}
|
||
}
|
||
if (window.stream) {
|
||
window.stream.getTracks().forEach(track => {
|
||
track.stop()
|
||
})
|
||
}
|
||
navigator.mediaDevices.getUserMedia(constraints)
|
||
.then(function(stream) {
|
||
var video = document.querySelector('video')
|
||
video.setAttribute('playsinline', true)
|
||
// 旧的浏览器可能没有srcObject
|
||
window.stream = stream
|
||
if ('srcObject' in video) {
|
||
video.srcObject = stream
|
||
} else {
|
||
// 防止在新的浏览器里使用它,应为它已经不再支持了
|
||
video.src = window.URL.createObjectURL(stream)
|
||
}
|
||
video.play()
|
||
})
|
||
.catch(function(err) {
|
||
alert(err.name + ': ' + err.message)
|
||
}) // 总是在最后检查错误
|
||
}, false)
|
||
this.tackPhoto()
|
||
},
|
||
methods: {
|
||
// 拍照
|
||
tackPhoto() {
|
||
this.context = this.$refs.refCanvas.getContext('2d') // 画布
|
||
this.timer = setInterval(() => {
|
||
this.countdown += 1
|
||
if (this.countdown > 30) {
|
||
clearInterval(this.timer)
|
||
this.timer = null
|
||
this.msg = '验证失败,请重新打开此页面'
|
||
}
|
||
this.context.drawImage(this.$refs.refVideo, 0, 0, this.screenSize.width, this.screenSize.height)
|
||
// // 保存为base64格式
|
||
this.imgUrl = this.saveAsPNG(this.$refs.refCanvas)
|
||
// /** 拿到base64格式图片之后就可以在this.compare方法中去调用后端接口比较了,也可以调用getBlobBydataURI方法转化成文件再去比较
|
||
// * 我们项目里有一个设置个人头像的地方,先保存一下用户的图片,然后去拿这个图片的地址和当前拍照图片给后端接口去比较。
|
||
// * */
|
||
var _this = this
|
||
var USERAVATARURL = this.imgUrl.substring(this.imgUrl.indexOf('base64,') + 7)
|
||
$.ajax({
|
||
xhrFields: {
|
||
withCredentials: true
|
||
},
|
||
type: 'POST',
|
||
url: config.httpurl + '/app/user/compareFaceForH5',
|
||
data: {
|
||
USERAVATARURL: USERAVATARURL,
|
||
USER_ID: this.USER_ID
|
||
},
|
||
dataType: 'json',
|
||
success: function(data) {
|
||
var result = data.result
|
||
if (result == 'success') {
|
||
clearInterval(_this.timer)
|
||
_this.timer = null
|
||
_this.msg = '验证成功'
|
||
_this.showContainer = false
|
||
} else if (result == 'error') {
|
||
_this.msg = data.msg
|
||
}
|
||
},
|
||
fail: function(data) {
|
||
_this.msg = '验证失败'
|
||
}
|
||
})
|
||
// this.msg = USERAVATARURL
|
||
}, 2000)
|
||
},
|
||
// 保存为png,base64格式图片
|
||
saveAsPNG(c) {
|
||
return c.toDataURL('image/png', 0.3)
|
||
},
|
||
// 根据url参数名称获取参数值
|
||
getUrlKey: function(name) {
|
||
return decodeURIComponent(
|
||
(new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.href) || [, ''])[1].replace(/\+/g, '%20')) || null
|
||
}
|
||
}
|
||
})
|
||
</script>
|
||
|