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

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

这个项目一开始的时候,点击图片我是使用的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>
相关推荐
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅6 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊6 小时前
jwt介绍
前端
爱敲代码的小鱼6 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax