使用案例:
<div class="ocr-camera" v-show="showCamera">
<div class="camera-head">
<span>{{ faceType }}人脸识别</span>
</div>
<div class="camera" id="camera">
<video style="width: 100%; height: 100%; object-fit: cover" id="video" autoplay="autoplay" playsinline></video>
<div class="mask-view">
<div>请将正脸移入框内</div>
</div>
</div>
<div class="camera-button">
<span style="font-size: 15px" @click="cancel">取消</span>
<svg class="icon" aria-hidden="true" style="height: 65px; width: 65px" @click="takePicture">
<use xlink:href="#icon-paizhao-copy"></use>
</svg>
<svg class="icon" aria-hidden="true" style="height: 40px; width: 40px" @click="switchCamera">
<use xlink:href="#icon-qiehuanshexiangtou-copy"></use>
</svg>
</div>
<!--描绘video截图-->
<canvas v-show="showPicture" id="canvas" width="300" height="300" style="position: fixed; left: 10000px"></canvas>
</div>
//mounted中
this.video = document.getElementById("video");
this.canvas = document.getElementById("canvas");
this.context = this.canvas.getContext("2d");
// 老的浏览器可能根本没有实现 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);
});
};
}
//methods中:
//开始拍照
checkFace(typeName,type,index) {
//默认使用前摄像头,强制使用后置摄像头如下设置
// let constraints = {video: { facingMode: { exact: "environment" } }};
if (this.stream) {
this.stream.getTracks().forEach((track) => {
track.stop();
});
}
let constraints = { video: { facingMode: "user" } };
navigator.mediaDevices
.getUserMedia(constraints)
.then((stream) => {
this.stream = stream;
// 旧的浏览器可能没有srcObject
if ("srcObject" in this.video) {
this.video.srcObject = stream;
this.video.setAttribute('playsinline', true)
this.video.play()
} else {
// 防止在新的浏览器里使用它,应为它已经不再支持了
this.video.src = window.URL.createObjectURL(stream);
this.video.setAttribute('playsinline', true)
this.video.play()
}
this.video.onloadedmetadata = (e) => {
this.video.play();
};
})
.catch(function (err) {
console.log(err.name + ": " + err.message);
});
this.showCamera = true;
},
//切换前后置
switchCamera() {
let constraints = { video: { facingMode: "user" } };
if (this.isFrontCamera) {
constraints = {
video: {
facingMode: { exact: "environment" },
},
};
}
if (this.stream) {
this.stream.getTracks().forEach((track) => {
track.stop();
});
}
navigator.mediaDevices
.getUserMedia(constraints)
.then((stream) => {
this.stream = stream;
this.isFrontCamera = !this.isFrontCamera;
// 旧的浏览器可能没有srcObject
if ("srcObject" in this.video) {
this.video.srcObject = stream;
this.video.setAttribute('playsinline', true)
this.video.play()
} else {
// 防止在新的浏览器里使用它,应为它已经不再支持了
this.video.src = window.URL.createObjectURL(stream);
this.video.setAttribute('playsinline', true)
this.video.play()
}
this.video.onloadedmetadata = (e) => {
this.video.play();
};
})
.catch(function (err) {
alert(err.name + ": " + err.message);
console.log(err.name + ": " + err.message);
});
},
takePicture() {//点击拍摄按钮,并生成照片文件
console.log(this.video.videoWidth, this.video.videoHeight);
this.canvas.width = this.video.videoWidth;
this.canvas.height = this.video.videoHeight;
//绘制画面
this.context.drawImage(
this.video,
0,
0,
this.canvas.width,
this.canvas.height
);
// canvas 转图片,再base64转流
let blob = baseTofile(this.canvas.toDataURL("image/png"));
function blobtoFile({ type } = blob) {
console.log(type, "type");
return new File([blob], "人脸照片文件", { type });
}
this.showCamera = false;
this.showDialog = true;
//上传结果,掉接口识别,等待结果返回后,去掉弹框
this.idNamePhotoCheck(blobtoFile(blob))
},
// base64格式转文件流
export function baseTofile(base64) {
var bytes = window.atob(base64.split(",")[1]); //去掉url的头,并转换为byte
//处理异常,将ascii码小于0的转换为大于0
var ab = new ArrayBuffer(bytes.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: "image/png" });
}
踩坑记录1:
在浏览器中调用是正常的,但是在ios系统的微信内置浏览器中调用时,摄像头就唤醒不成功,原因:ios不会自动播放video
解决方案:
添加如下代码即可
//这个属性是ios微信浏览器设置可以让视频在小窗口内播放,也就是不是全屏播放
this.video.setAttribute('playsinline', true)
//播放
this.video.play()
踩坑记录2:
当在A页面调用相机前置摄像头拍照后,通过路由跳转至B页面,此时再次调用相机后置摄像头黑屏或失败 ,原因:当前设备被占用
解决方案:
在A页面销毁时,如果相机设备被占用,就将它释放掉
在每次调用相机前,先关闭相机设备
destroyed(){
if (this.stream) {
this.stream.getTracks().forEach((track) => {
track.stop();
});
}
},