是这样的,我们准备上小程序,本来是打算用webview的形式引入h5拍照,但是后来突然提出要价格遮罩,而且时间很紧急,顺便吐槽一下,改需求跟喝汤一样,一句话就让我改,时间只有一天,心累。
但是感觉是逃不掉了,研究了下决定用uniapp来做遮罩拍照,查了一下发现uniapp的标签可以调出摄像头,
可以放入图片做一个遮罩,
可以自定义一些例如按钮,提示语之类的块,
效果图大概这样,红色的部分可以放一个拍照按钮:
标签部分:
<template>
<view class="container">
<cover-view class="square"></cover-view>
<camera device-position="front" flash="auto" @error="error" style="width: 100%; height: 90vh;overflow: hidden;">
<cover-image src="../../static/camera/scan-img.png" class="scan-img"></cover-image>
<cover-view class="tip">
<cover-view class="circle-border">
<cover-view class="scan-text">请按图示将人脸放人取景框中</cover-view>
<cover-view class="circle-wrap">
<cover-view class="circle" @tap="takePhoto"></cover-view>
</cover-view>
</cover-view>
</cover-view>
</camera>
<canvas class="canvas" :style="{ width: canvasSize.width,height: canvasSize.height}" canvas-id="myCanvas"></canvas>
</view>
</template>
js部分代码:
<script>
export default {
data() {
return {
src: "",
pic:'',
canvasSize: {
width: 0,
height: 0
},
screenH:0,
screenW:0,
topView:0,
botView:0,
}
},
onLoad(){
this.shouquan();
this.screenH = uni.getSystemInfoSync().screenHeight//获取手机屏幕高度
this.screenW = uni.getSystemInfoSync().screenWidth//获取手机屏幕宽度
this.getTopHeight();
this.getBotHeight()
},
methods: {
getTopHeight(dom){//获取摄像部分距离顶部的高度
let _this = this;
setTimeout(() => {
uni.getSystemInfo({
success: function(res) {
let info = uni.createSelectorQuery().selectAll(".square");
info.fields({
size: true,
scrollOffset: true
}, (data) => { // 获取距离
_this.topView = parseInt(data[0].height);
// let firstContentScroll = data[0].height + data[1].height;
// let srollPullDown = firstContentScroll + 500;
// e[0](srollPullDown);
}).exec();
}
});
})
},
getBotHeight(){//获取摄像部分距离底部的高度
let _this = this;
setTimeout(() => {
uni.getSystemInfo({
success: function(res) {
let info = uni.createSelectorQuery().selectAll(".tip");
info.fields({
size: true,
scrollOffset: true
}, (data) => { // 获取距离
_this.botView = parseInt(data[0].height);
// let firstContentScroll = data[0].height + data[1].height;
// let srollPullDown = firstContentScroll + 500;
// e[0](srollPullDown);
}).exec();
}
});
})
},
shouquan(){//用户授权拍照
uni.getSetting({
success(res) {
if (!res.authSetting['scope.camera']) {
uni.authorize({scope: 'scope.camera'})
}
}
})
},
// 获取图片信息
getImageInfo(src){
return new Promise((resolve, reject)=>{
uni.getImageInfo({
src,
success: (info)=> {
resolve(info);
},
fail: () => {
reject(null);
}
});
});
},
// 压缩
compress(params) {
return new Promise(async (resolve, reject) => {
// 等待图片信息
let info = await this.getImageInfo(params.src).then(info=>info).catch(err=>err);
if(!info){
reject('获取图片信息异常');
return;
}
// 设置最大 & 最小 尺寸
const maxSize = params.maxSize || 960;
const minSize = params.minSize || 500;
// 截取图片的底部
// const botNumH = this.screenH/2;
const botNumW = this.screenH/2;
// 当前图片尺寸
let {width,height} = info;
const botNumH = this.screenH - this.topView - this.botView;//中间显示高度
// 非 H5 平台进行最小尺寸校验
// #ifndef H5
if(width <= minSize && height <= minSize){
resolve(params.src);
return;
}
// #endif
width = maxSize;
height = maxSize;
// 最大尺寸计算
// if (width > maxSize || width < minSize|| height > maxSize) {
// if (width > height) {
// height = Math.floor(height / (width / maxSize));
// } else {
// width = Math.floor(width / (height / maxSize));
// if(width < 500){
// width = 500;
// }
// }
// }
// height = 500;
// width = 500;
// 设置画布尺寸
this.$set(this,"canvasSize",{
width: `${width}rpx`,
height: `${height}rpx`
});
// Vue.nextTick 回调在 App 有异常,则使用 setTimeout 等待DOM更新
setTimeout(() => {
const ctx = uni.createCanvasContext('myCanvas', this);
ctx.clearRect(0,0,width, height)
ctx.drawImage(info.path, 0, uni.upx2px(botNumH), uni.upx2px(width), uni.upx2px(height));
ctx.draw(false, () => {
uni.canvasToTempFilePath({
x: 0,
y: uni.upx2px(botNumH),
width: uni.upx2px(width),
height: uni.upx2px(height),
destWidth: width,
destHeight: height,
canvasId: 'myCanvas',
fileType: params.fileType || 'jpeg',
quality: params.quality || 1,
success: (res) => {
// 在H5平台下,tempFilePath 为 base64
resolve(res.tempFilePath);
},
fail:(err)=>{
reject(null);
}
},this);
});
}, 300);
});
},
takePhoto() {
const ctx = uni.createCameraContext();
let _this = this;
ctx.takePhoto({
quality: 'high',
success: (res) => {
let file = res.tempImagePath;
this.compress({
src:file,
maxSize:960,
fileType:'jpg',
quality:0.6,
minSize:500
}).then(res=>{
_this.uploadFile(res)
})
}
});
},
uploadFile(file){
uni.uploadFile({
url: 'https://xxxxxxxxx', //上传服务器地址
filePath: file,
name: 'file',
formData: {
'test': 'test'
},
success: (res) => {
//上传成功
let data = JSON.parse(res.data);
if(data.code == 200){
// this.src= data.webUrl;
let pages = getCurrentPages();//返回上一页
let prevPage = pages[pages.length - 2];
uni.navigateBack()
}else{
alert(data.msg)
}
},
fail: function(t) {
//上传失败
console.log(t)
},
})
}
}
}
</script>
css部分:
<style>
.square{
height:200rpx;
background:#fff;
}
.container{
width:100%;
height:100vh;
overflow: hidden;
background:#4b4956;
}
.canvas{
visibility: hidden;
position:absolute;
left:-2000px;
}
.scan-img{
/* opacity: 0.4; */
width: 100%;
height:50vh;
}
.tip{
position:absolute;
bottom:0;
width:100%;
height:40vh;
/* z-index:10; */
background:#4b4956;
}
.scan-text{
width:100%;
font-size: 18px;
color:#fff;
text-align: center;
}
.circle-border{
position: absolute;
bottom:40rpx;
width:100%;
height:300rpx;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.circle-wrap{
width:100%;
height:120px;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.circle{
width:120rpx;
height:120rpx;
background:#fff;
border:5px solid #ccc;
border-radius: 50%;
transform: all 1s;
}
.circle:active{
width:90rpx;
height:90rpx;
}
</style>