这周做小程序的时候有个需求,在小程序实人认证的时候需要上传身份证的正反面,为了让限制拍摄区域,我们要实现拍照时,身份证的取景框。完成辅助定位的作用。 下面是具体的实现图片
这个项目一开始的时候,点击图片我是使用的wx.chooseMedia
这个api来实现的,但是这个方法没有办法对用户拍照进行限制,也就无法达到我们的要求(实现身份证取景框
),接着在网上找到另一种方法。
js
// 上传认证图片
const uploadImg = (valNum) => {
wx.chooseMedia({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
mediaType: ['image'],
success: function (res) {
// 显示loading
wx.showLoading({
title: '加载中',
mask: true,
});
let tempFilePath = res.tempFiles[0].tempFilePath;
if (valNum === 1) idCard1.value = tempFilePath;
if (valNum === 2) idCard2.value = tempFilePath;
// 检测图片尺寸,如果过大则必须压缩
wx.getImageInfo({
src: tempFilePath,
complete: async function (res) {
// 无论宽高,最大值
let maxPX = 700;
// 初始化目标尺寸为原始尺寸
let targetWidth = res.weight;
let targetHeight = res.height;
// 计算目标尺寸,等比缩放
if (res.width > res.height) {
// 横向图片
if (res.width >= maxPX) {
// 原始图片宽度大于maxPX,则需要缩小,将目标宽度设置为maxPX
targetWidth = maxPX;
targetHeight = Math.round((targetWidth * res.height) / res.width);
}
// 原始图片宽度小于maxPX,则使用原始尺寸
} else {
// 纵向图片
if (res.height >= maxPX) {
// 原始图片高度大于maxPX,则需要缩小,将目标高度设置为maxPX
targetHeight = maxPX;
targetWidth = Math.round((targetHeight * res.width) / res.height);
}
// 原始图片高度小于maxPX,则使用原始尺寸
}
// 压缩图片
wx.compressImage({
src: tempFilePath,
compressedWidth: targetWidth,
compressHeight: targetHeight,
complete: async function (successRes) {
// 关闭loading
wx.hideLoading();
if (successRes.errMsg != 'compressImage:ok') {
util.showError('压缩图片失败');
return;
}
// 将图片保存到服务器
uploadIdCard(successRes.tempFilePath, valNum);
},
});
},
});
},
});
};
使用caram标签
我们使用上面的方法时候,是没办法完成身份证取景框的要求。要想自定义身份证取景框,就需要使用caram标签来实现。在这里,开发过程中也会出现一些小问题,下面是我的一下整理:
-
当在一个页面单独使用caram标签的时候,不能使用v-if来动态的隐藏和显示caram标签。
当我们在页面中使用v-if来隐藏caram标签的时候,在进行真机调试时界面中会出现这个错误:
operateCamera:fail camera has not been initialized
,解决办法可以把caram标签放在组件内,然后控制组件的显示与隐藏 -
需要自定义拍照事件和选取相册照片事件
js
onMounted( () => {
if (uni.createCameraContext) {
cameraContext.value = uni.createCameraContext();
} else {
// 如果希望用户在最新版本的客户端上体验您的小程序,可以这样子提示
uni.showModal({
title: '提示',
content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。',
});
}
})
// 自定义拍照事件
const takePhoto = () => {
cameraContext.value.takePhoto({
quality: 'normal',
success: (res) => {
uni.showToast({
title: '拍照成功',
icon: 'none',
duration: 1200,
});
res.tempImagePath // 这里是拍照图片的临时目录
},
fail: (err) => {
uni.showToast({
title: '拍照失败,请检查系统是否授权',
icon: 'none',
duration: 1200,
});
console.log('图片拍照失败', err);
},
});
// 自定义相册选取图片
// 从相册选取
const chooseImage = () => {
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album'],
success: (res) => {
console.log('相册选取成功', res);
emit('getImgPath', res.tempFilePaths[0]);
},
fail: (err) => {
console.log('相册选取失败', err);
},
});
};
上面的代码中推荐配合wx.getImageInfo
使用来限制上传图片的类型。
- 创建组件,自定义实现身份证取景框
js
<template>
<view>
<view :style="{ height: windowHeight + 'px' }">
<camera mode="normal" device-position="back" flash="off" :style="{ height: cameraHeight + 'px' }">
<cover-view class="controls" style="width: 100%; height: 100%">
<!-- 头像面 -->
<cover-image
v-if="props.imgType == 1"
class="w569-h828"
src="https://rizuwang-1316974425.cos.ap-beijing.myqcloud.com/mobilePics/idCardMask1.png" />
<!-- 国徽面 -->
<cover-image
v-if="props.imgType == 2"
class="w569-h828"
src="https://rizuwang-1316974425.cos.ap-beijing.myqcloud.com/mobilePics/idCardMask2.png" />
</cover-view>
</camera>
<view class="bottom font-36-fff">
<view class="wrap">
<view class="back" @click="close">取消</view>
<view @click="takePhoto">
<image class="w131-h131" src="../static/take_camera_btn_icon.png"> </image>
</view>
<view @click="chooseImage"> 相册 </view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
const cameraHeight = ref('');
const windowHeight = ref('');
const cameraContext = ref({});
const props = defineProps({
imgType: Number,
});
const emit = defineEmits(['getImgPath', 'colseCamera']);
onMounted(() => {
if (uni.createCameraContext) {
cameraContext.value = uni.createCameraContext();
} else {
// 如果希望用户在最新版本的客户端上体验您的小程序,可以这样子提示
uni.showModal({
title: '提示',
content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。',
});
}
const systemInfo = uni.getSystemInfoSync();
windowHeight.value = systemInfo.windowHeight;
cameraHeight.value = systemInfo.windowHeight - 80;
console.log('有没有:', uni.onGyroscopeChange);
// 初始化陀螺仪传感器监听
});
const takePhoto = () => {
cameraContext.value.takePhoto({
quality: 'normal',
success: (res) => {
uni.showToast({
title: '拍照成功',
icon: 'none',
duration: 1200,
});
emit('getImgPath', res.tempImagePath);
},
fail: (err) => {
uni.showToast({
title: '拍照失败,请检查系统是否授权',
icon: 'none',
duration: 1200,
});
console.log('图片拍照失败', err);
},
});
};
// 从相册选取
const chooseImage = () => {
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album'],
success: (res) => {
console.log('相册选取成功', res);
emit('getImgPath', res.tempFilePaths[0]);
},
fail: (err) => {
console.log('相册选取失败', err);
},
});
};
// 关闭相机
const close = () => {
emit('colseCamera', false);
};
</script>
<style lang="scss" scoped>
.icon-w569-h828 {
width: 569rpx;
height: 828rpx;
}
.controls {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.bottom {
width: 100%;
background-color: #000;
.wrap {
display: flex;
align-items: center;
justify-content: space-between;
height: 80px;
padding: 0 73rpx;
}
}
.w569-h828 {
width: 569rpx;
height: 828rpx;
}
.w131-h131 {
width: 131rpx;
height: 131rpx;
}
.font-36-fff {
font-size: 36rpx;
color: #fff;
}
</style>
到这里这个需求就实现了,我们又添加了一个显示用户手机拍照的需求。实际上就是利用陀螺仪来进行判断
使用陀螺仪显示用户拍照方向
具体的实现效果如下:
首先我们需要了解wx.startDeviceMotionListening
官方文档
下面是具体的代码实现:
js
<template>
<view>
<view :style="{ height: windowHeight + 'px' }">
<camera mode="normal" device-position="back" flash="off" :style="{ height: cameraHeight + 'px' }">
<cover-view class="controls" style="width: 100%; height: 100%">
<!-- 头像面 -->
<cover-image
v-if="props.imgType == 1"
class="w569-h828"
src="https://rizuwang-1316974425.cos.ap-beijing.myqcloud.com/mobilePics/idCardMask1.png" />
<!-- 国徽面 -->
<cover-image
v-if="props.imgType == 2"
class="w569-h828"
src="https://rizuwang-1316974425.cos.ap-beijing.myqcloud.com/mobilePics/idCardMask2.png" />
</cover-view>
</camera>
<view class="msg" v-if="!cameraHeight" :style="{ lineHeight: windowHeight + 'px' }">请横屏,端平手机</view>
<view class="bottom font-36-fff">
<view class="wrap">
<view class="back" @click="close">取消</view>
<view @click="takePhoto">
<image class="w131-h131" src="../static/take_camera_btn_icon.png"> </image>
</view>
<view @click="chooseImage"> 相册 </view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
const cameraHeight = ref('');
const windowHeight = ref('');
const cameraContext = ref({});
const props = defineProps({
imgType: Number,
isGyroscope: Boolean,
});
const emit = defineEmits(['getImgPath', 'colseCamera']);
onMounted(() => {
if (uni.createCameraContext) {
cameraContext.value = uni.createCameraContext();
} else {
// 如果希望用户在最新版本的客户端上体验您的小程序,可以这样子提示
uni.showModal({
title: '提示',
content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。',
});
}
const systemInfo = uni.getSystemInfoSync();
windowHeight.value = systemInfo.windowHeight;
cameraHeight.value = systemInfo.windowHeight - 80;
// 开启陀螺仪
if (props.isGyroscope) {
// 初始化陀螺仪传感器监听
// 取得设备方向
// 参考:https://developers.weixin.qq.com/miniprogram/dev/api/
wx.startDeviceMotionListening({
// 参考:https://developers.weixin.qq.com/miniprogram/dev/api/device/motion/wx.onDeviceMotionChange.html
interval: 'normal',
success: (res) => {
console.log(res);
},
fail: () => {
console.log('startDeviceMotionListening - fail');
},
});
wx.onDeviceMotionChange((res) => {
console.log('垂直于地面的 X 轴方向上的旋转角度', res.beta);
console.log('垂直于地面的 Y 轴方向上的旋转角度', res.gamma);
// 参考:https://developers.weixin.qq.com/miniprogram/dev/api/device/motion/wx.onDeviceMotionChange.html
// res.beta:垂直于地面的 X 轴方向上的旋转角度
// res.gamma:垂直于地面的 Y 轴方向上的旋转角度
if (Math.abs(res.beta) > 30 || Math.abs(res.gamma) > 90) {
// 请横屏且端正手机拍照
cameraHeight.value = 0;
} else {
cameraHeight.value = windowHeight.value - 80;
}
});
}
});
onBeforeUnmount(() => {
uni.stopGyroscope({
success() {
console.log('stop success!');
},
fail() {
console.log('stop fail!');
},
});
wx.stopDeviceMotionListening();
});
// 侦听陀螺仪开关
watch(props.isGyroscope, (oldVal, newVal) => {
console.log('监听的新旧值', oldVal, newVal);
});
const takePhoto = () => {
if (cameraHeight.value == 0)
// 如果不是横屏模式则不拍照
return;
cameraContext.value.takePhoto({
quality: 'normal',
success: (res) => {
uni.showToast({
title: '拍照成功',
icon: 'none',
duration: 1200,
});
emit('getImgPath', res.tempImagePath);
},
fail: (err) => {
uni.showToast({
title: '拍照失败,请检查系统是否授权',
icon: 'none',
duration: 1200,
});
console.log('图片拍照失败', err);
},
});
};
// 从相册选取
const chooseImage = () => {
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album'],
success: (res) => {
console.log('相册选取成功', res);
emit('getImgPath', res.tempFilePaths[0]);
},
fail: (err) => {
console.log('相册选取失败', err);
},
});
};
// 关闭相机
const close = () => {
emit('colseCamera', false);
};
</script>
<style lang="scss" scoped>
.icon-w569-h828 {
width: 569rpx;
height: 828rpx;
}
.controls {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.bottom {
width: 100%;
background-color: #000;
.wrap {
display: flex;
align-items: center;
justify-content: space-between;
height: 80px;
padding: 0 73rpx;
}
}
.w569-h828 {
width: 569rpx;
height: 828rpx;
}
.w131-h131 {
width: 131rpx;
height: 131rpx;
}
.font-36-fff {
font-size: 36rpx;
color: #fff;
}
.msg {
width: 100%;
height: 100%;
font-size: 50rpx;
text-align: center;
}
</style>