身份证上传图片,自定义身份证取景框

这周做小程序的时候有个需求,在小程序实人认证的时候需要上传身份证的正反面,为了让限制拍摄区域,我们要实现拍照时,身份证的取景框。完成辅助定位的作用。 下面是具体的实现图片

这个项目一开始的时候,点击图片我是使用的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标签来实现。在这里,开发过程中也会出现一些小问题,下面是我的一下整理:

  1. 当在一个页面单独使用caram标签的时候,不能使用v-if来动态的隐藏和显示caram标签。

    当我们在页面中使用v-if来隐藏caram标签的时候,在进行真机调试时界面中会出现这个错误: operateCamera:fail camera has not been initialized,解决办法可以把caram标签放在组件内,然后控制组件的显示与隐藏

  2. 需要自定义拍照事件和选取相册照片事件

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使用来限制上传图片的类型。

  1. 创建组件,自定义实现身份证取景框
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>
相关推荐
&白帝&1 小时前
Vue.js 过渡 & 动画
前端·javascript
总是学不会.1 小时前
SpringBoot项目:前后端打包与部署(使用 Maven)
java·服务器·前端·后端·maven
Fanfffff7202 小时前
深入探索Vue3组合式API
前端·javascript·vue.js
光影少年2 小时前
node配置swagger
前端·javascript·node.js·swagger
昱禹2 小时前
关于CSS Grid布局
前端·javascript·css
啊QQQQQ2 小时前
HTML:相关概念以及标签
前端·html
就叫飞六吧3 小时前
vue2和vue3全面对比
前端·javascript·vue.js
Justinc.3 小时前
CSS基础-盒子模型(三)
前端·css
qq_2518364574 小时前
基于ssm vue uniapp实现的爱心小屋公益机构智慧管理系统
前端·vue.js·uni-app
._Ha!n.4 小时前
Vue基础(二)
前端·javascript·vue.js