uniapp人脸遮罩拍照

2,267 阅读1分钟

是这样的,我们准备上小程序,本来是打算用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>