由于浏览器的机制,只能在https中才能正常使用(注意注意)
一、组件
<template>
<view class="root">
<view class="canvasBox">
<template v-if="!isUse">
<slot name="error">
<view class="error">
<view class="on1">相机权限被拒绝,请尝试如下操作:</view>
<view>· 刷新页面后重试;</view>
<view>· 在系统中检测当前App或浏览器的相机权限是否被禁用;</view>
<view>· 如果依然不能体验,建议在微信中打开链接;</view>
</view>
</slot>
</template>
</view>
</view>
</template>
<script>
export default {
props: {
continue: {
type: Boolean,
default: false, // false 监听一次 true 持续监听
},
exact: {
type: String,
default: "user", // environment 后摄像头 user 前摄像头
},
size: {
type: String,
default: "whole", // whole 全屏 balf 半屏
},
definition: {
type: Boolean,
default: false, // fasle 正常 true 高清
},
},
data() {
return {
windowWidth: 0,
windowHeight: 0,
video: null,
canvas2d: null,
canvasWidth: 400,
canvasHeight: 400,
maskWidth: 400,
maskHeight: 400,
inter: 0,
isParse: false,
isUse: true,
};
},
mounted() {
if (origin.indexOf("https") === -1)
throw "请在 https 环境中使用摄像头组件。";
this.windowWidth =
document.documentElement.clientWidth || document.body.clientWidth;
this.windowHeight =
document.documentElement.clientHeight || document.body.clientHeight;
this.windowHeight =
this.size === "whole" ? this.windowHeight : this.windowHeight / 2;
this.isParse = true;
this.$nextTick(() => {
this.openScan();
});
},
destroyed() {
this.closeCamera();
},
methods: {
takePhoto() {
console.log("takePhoto");
const that = this;
if (!that.isParse) return;
if (that.video.readyState === that.video.HAVE_ENOUGH_DATA) {
that.canvas2d.drawImage(
that.video,
0,
0,
that.transtion(400),
that.transtion(400)
);
const tempSrc = that.canvas.toDataURL("image/png");
return tempSrc;
}
},
openScan() {
const width = this.transtion(this.windowHeight);
const height = this.transtion(this.windowWidth);
const videoParam = {
audio: false,
video: {
facingMode: {
exact: this.exact,
},
width: 200,
height: 200,
},
};
navigator.mediaDevices
.getUserMedia(videoParam)
.then((stream) => {
this.video = document.createElement("video");
this.canvas = document.createElement("canvas");
this.canvas.id = "canvas";
this.canvas.width = this.transtion(this.canvasWidth);
this.canvas.height = this.transtion(this.canvasHeight);
this.canvas.style = "display:none;";
this.canvas2d = this.canvas.getContext("2d");
const canvasBox = document.querySelector(".canvasBox");
const root = document.querySelector(".root");
canvasBox.append(this.video);
root.append(this.canvas);
this.video.srcObject = stream;
this.video.setAttribute("playsinline", true);
this.video.setAttribute("class", "video");
this.video.play();
})
.catch((err) => {
this.isUse = false;
this.$emit("error", err);
});
},
closeCamera() {
this.isParse = false;
},
transtion(number) {
return this.definition ? number * 2.8 : number * 1.8;
},
},
};
</script>
<style scoped>
page {
background-color: #fff;
}
.canvasBox {
margin: 100rpx auto 0 !important;
width: 210px;
height: 210px;
transform: rotateY(180deg);
z-index: 10;
border-radius: 100%;
overflow: hidden;
}
.video {
width: 100%;
height: 100%;
}
.error {
color: #fff;
padding: 40rpx;
font-size: 24rpx;
background-color: #333333;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 550rpx;
border-radius: 20rpx;
}
.error .on1 {
font-size: 30rpx;
}
</style>
二、调用(此处调用得到的是一个base64文档流,先调用上传接口拿到url地址再入库)
<template>
<view class="wrap">
<view class="main">
<view class="hint">请将脸移入框内</view>
<!-- <view>
<img class="img" src="@/static/user/video_img.gif" alt="" />
</view> -->
<view class="imageview pr">
<camera-components
ref="camera"
v-if="!tempSrc"
@error="qrcodeError"
></camera-components>
<view
class="pac"
v-if="tempSrc"
style="
height: 240px;
width: 240px;
border-radius: 100%;
z-index: 999;
"
>
<image
class="pac"
style="margin: auto; height: 230px"
:src="vuex_imgUrl + imageUrl"
mode="widthFix"
></image>
</view>
</view>
</view>
<button
v-if="isPicture"
style="margin-top: 150rpx; background-color: #fff; width: 40%"
@click="takePhoto"
>
拍照
</button>
</view>
</template>
<script>
import cameraComponents from "./components/camera.vue";
import { init_driverApprove, init_userAutherInfo } from "@/api/user.js";
export default {
name: "StartCheckLive",
components: {
cameraComponents,
},
data() {
return {
vuex_imgUrl: this.$store.state?.vuex_imgUrl,
isPicture: true,
tempSrc: "",
imageUrl: "",
form: {},
};
},
onLoad() {
this.getDate();
this.isPicture = true;
},
methods: {
takePhoto() {
const that = this;
that.tempSrc = that.$refs.camera.takePhoto();
if (that.tempSrc) {
//调用上传图片转换文档流获取url
uni.uploadFile({
url: that.$store.state?.vuex_baseUrl + "/common/upload",
filePath: that.tempSrc,
name: "file",
success: (uploadFileRes) => {
console.log(uploadFileRes);
let imgData = JSON.parse(uploadFileRes.data);
if (imgData.code == 200) {
uni.showToast({
mask: true,
icon: "none",
title: "上传成功",
});
that.imageUrl = imgData?.fileName;
this.setDate();
}
},
});
}
},
qrcodeError(err) {
uni.showModal({
title: "摄像头授权失败",
content: "摄像头授权失败,请检测当前浏览器是否有摄像头权限。",
success: () => {
uni.navigateBack({});
},
});
},
async setDate() {
this.form.face = this.imageUrl;
let params = {
...this.form,
};
let res = await init_driverApprove(params);
this.isPicture = false;
uni.reLaunch({
url: "/pages/tabbar/home",
});
},
async getDate() {
let res = await init_userAutherInfo();
this.form = res;
},
},
};
</script>
<style lang="less" scoped>
body {
min-height: 100vh;
background: #fff;
}
.title_top {
width: 100%;
height: 46px;
line-height: 46px;
text-align: center;
font-size: 18px;
font-weight: 700;
background: #000000;
color: #ffffff !important;
}
.van-nav-bar__title {
color: #ffffff !important;
}
.van-nav-bar__title van-ellipsis {
color: #ffffff !important;
}
.wrap {
min-height: 100vh;
background: #000000;
position: relative;
}
.main {
width: 100%;
height: 500px;
position: relative;
display: flex;
justify-content: center;
align-items: center;
.hint {
position: absolute;
top: 60px;
left: 0;
right: 0;
text-align: center;
z-index: 10;
color: #fff;
font-size: 20px;
font-weight: 700;
z-index: 1001;
}
}
.img {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 500px;
z-index: 999;
overflow: hidden;
overflow: hidden;
border-radius: 100%;
}
.imageview {
width: 280px;
height: 280px;
background-color: #999999;
border-radius: 100px;
margin: 30px auto;
overflow: hidden;
position: relative;
z-index: 9;
}
.pac {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
三、效果(如下是再H5之中的效果)