APP自定义身份证相机(Android +iOS)

news2024/11/17 11:40:40

基本上同时兼容安卓和苹果的插件都需要付费,这里我找了2个好用的免费插件

1.仅支持安卓:自定义身份证相机(支持蒙版自定义),内置蒙版,照片预览,身份证裁剪 - DCloud 插件市场、

2.支持iOS(已测),支持Android(未测,应该也可以用):

 自定义相机 - DCloud 插件市场

第一个插件使用方法(仅支持安卓):

创建一个realName.vue文件

<view class="personalInfo" style="margin-top: 20rpx;">
        <view class="label" style="margin-bottom: 8rpx;">
            <view class="blue"></view>
            <view class="title">证件图片</view>
        </view>
        <view class="tips">请拍摄或上传身份证原件照片,确保照片完整清晰</view>
        <view class="imgBox">
            <view class="front">
                <image :src="frontImg" :class="platform == 'ios' ? 'transformImg' : ''" @click="uploadImgNew('front')"/>
                <view class="frontBtn" @click="uploadImgNew('front')" v-if="frontImg == ''">
                    上传人像面
                </view>
            </view>
            <view class="back">
                <image :src="backImg" :class="platform == 'ios' ? 'transformImg' : ''" @click="uploadImgNew('back')"/>
                <view class="backBtn" @click="uploadImgNew('back')" v-if="backImg == ''">
                    上传国徽面
                </view>
            </view>
        </view>
    </view>
export default {
  data () {
    return {
        frontImg: '',
        backImg: '',
		canvasSiz:{
			width:188,
			height:273
		},
        flag: true
    }
  },
methods:{
    uploadImgNew(types){
        console.log('打开相机');
        let platform = uni.getSystemInfoSync().platform
        if(!this.flag){
            uni.showToast({
                title: '图片正在上传中,请误做其他操作', 
                icon: 'none'
            })
            return
        }
        if(platform == 'ios'){
            // ios的另外用了别的插件,下面会讲到
            uni.navigateTo({
                url: `./idcard?dotype=${types == 'front' ? 'face' : 'badge'}`
            })
        }else{
            var cameraModule = uni.requireNativePlugin('yun-camerax-module');
            //无需蒙版可将type设置为非参数值,例如 type:99
            cameraModule.takePhoto({ 
                type: types == 'front' ? 0 : 1, 
                imageIndex: 2, fullSrc: true,
                text: types == 'front' ? '将身份证正面置于此区域内并对齐边缘' : '将身份证背面置于此区域内并对齐边缘'
            }, res => {
                console.log(res);
                uni.showModal({
                    title: '提示',
                    // content: JSON.stringify(res),
                    content: '请再次确认使用该图',
                    success: (res1) => {
                        if (res1.confirm) {
                            console.log('用户点击确定',res);
                            this.upLoadImg(res.file,types)
                        } else if (res1.cancel) {
                            console.log('用户点击取消');
                        }
                    }
                });
            });
        }
    },
    async upLoadImg(path,type) {
      setTimeout(()=>{
        uni.showToast({
              title: '上传中',
              icon: 'none'
        })
        this.flag = false
        uni.uploadFile({
          url: 'xxxxxx/upload', //后端上传接口
          filePath: path,
          name: "file",
          success: (res) => {
            console.log('res222',res);
            if(JSON.parse(res.data).code == 0){
                console.log('JSON.parse(res.data)',JSON.parse(res.data));
                if(type == 'front'){
                    this.frontImg = JSON.parse(res.data).data.rel_path
                    this.$forceUpdate()
                }else{
                    this.backImg = JSON.parse(res.data).data.rel_path
                    this.$forceUpdate()
                }
                this.flag = true   
            }else{
              uni.showToast({
                  title: JSON.parse(res.data).msg,
                  icon: 'none'
              })
            }
              
          },
          fail(e) {
            this.showTip("上传图片失败");
          },
        });
      },300)
    }, 

第二个插件使用方法:

需要用到live-pusher直播推流,在manifest.json中勾选,真机调试需要重新打自定义基座再重新运行

为了防止样式兼容问题,另外需配置如下:

在同级目录下创建idcard.nvue文件

然后把下面代码整个copy进去

<template>
	<view class="live-camera" :style="{ width: windowWidth, height: windowHeight }">
		<view class="preview" :style="{ width: windowWidth, height: windowHeight - 65 }">
			<live-pusher
				id="livePusher"
				ref="livePusher"
				class="livePusher"
				mode="FHD"
				beauty="0"
				whiteness="0"
				:aspect="aspect"
				min-bitrate="1000"
				audio-quality="16KHz"
				:device-position="back"
				:auto-focus="true"
				:muted="true"
				:enable-camera="true"
				:enable-mic="false"
				:zoom="false"
				@statechange="statechange"
				:style="{ width: cameraWidth, height: cameraHeight }"
			></live-pusher>

			<!--提示语-->
			<cover-view class="remind">
				<text class="remind-text" style="">{{ message }}</text>
			</cover-view>

			<!--辅助线-->
			<cover-view class="outline-box" :style="{ width: windowWidth, height: windowHeight - 80}">
				<cover-image
					class="outline-img"
					:src="dotype == 'face' ? '/static/live-camera/outline/idcardface.png' : '/static/live-camera/outline/idcardbadge.png'"
					style=""
				></cover-image>
			</cover-view>
		</view>

		<view class="menu">
			<!--底部菜单区域背景-->
			<cover-image class="menu-mask" src="/static/live-camera/bar.png"></cover-image>

			<!--返回键-->
			<cover-image class="menu-back" @tap="back" src="/static/live-camera/back.png"></cover-image>

			<!--快门键-->
			<cover-image class="menu-snapshot" @tap="snapshot" src="/static/live-camera/shutter.png"></cover-image>

			<!--反转键-->
			<cover-image class="menu-flip" @tap="flip" src="/static/live-camera/flip.png"></cover-image>
		</view>
	</view>
</template>

<script>
let _this = null;
export default {
	data() {
		return {
			poenCarmeInterval: null, //打开相机的轮询
			dotype: 'face', //操作类型
			message: '', //提示
			aspect: '2:3', //比例
			cameraWidth: '', //相机画面宽度
			cameraHeight: '', //相机画面宽度
			windowWidth: '', //屏幕可用宽度
			windowHeight: '', //屏幕可用高度
			camerastate: false, //相机准备好了
			livePusher: null, //流视频对象
			snapshotsrc: null //快照
		};
	},
	onLoad(e) {
		console.log('e',e);
		_this = this;
		this.dotype = e.dotype;
		this.initCamera();
	},
	onReady() {
		uni.showToast({
			title: '相机加载中...',
			icon: 'none',
			duration: 800
		})
		this.livePusher = uni.createLivePusherContext('livePusher', this);
		console.log('this.livePusher',this.livePusher);
		this.startPreview(); //开启预览并设置摄像头
		this.poenCarme();
	},
	methods: {
		//轮询打开
		poenCarme() {
			//#ifdef APP-PLUS
			if (plus.os.name == 'Android') {
				console.log('111');
				this.poenCarmeInterval = setInterval(function() {
					console.log(_this.camerastate);
					if (!_this.camerastate) _this.startPreview();
				}, 2500);
			}else{
				console.log('2222');
			}
			//#endif
		},
		//初始化相机
		initCamera() {
			//处理安卓手机异步授权问题
			uni.getSystemInfo({
				success: (res) => {
					console.log('resxxxx',res);
					this.windowWidth = res.windowWidth;
					this.windowHeight = res.windowHeight;
					this.cameraWidth = res.windowWidth;
					this.cameraHeight = res.windowWidth * 1.5;
				}
			});
		},

		//开始预览
		startPreview() {
			console.log('执行开始预览');
			this.livePusher.startPreview({
				success: (a) => {
					console.log('aaa',a);
				}
			});
		},

		//停止预览
		stopPreview() {
			this.livePusher.stopPreview({
				success: a => {
					console.log('停止预览',a);
					this.camerastate = false; //标记相机未启动
				}
			});
		},

		//状态
		statechange(e) {
			//状态改变
			console.log(e);
			if (e.detail.code == 1007) {
				_this.camerastate = true;
			} else if (e.detail.code == -1301) {
				_this.camerastate = false;
			}
		},

		//返回
		back() {
			uni.navigateBack();
		},

		//抓拍
		snapshot() {
			this.livePusher.snapshot({
				success: e => {
					console.log('快门',e);
					this.snapshotsrc = e.message.tempImagePath;
					this.stopPreview();
					this.setImage();
					uni.navigateBack();
				}
			});
		},

		//反转
		flip() {
			this.livePusher.switchCamera();
		},

		//设置
		setImage() {
			let pages = getCurrentPages();
			let prevPage = pages[pages.length - 2]; //上一个页面

			//直接调用上一个页面的setImage()方法,把数据存到上一个页面中去
			prevPage.$vm.setImage({ path: this.snapshotsrc, dotype: this.dotype });
		}
	}
};
</script>

<style lang="scss">

.live-camera {
	.preview {
		justify-content: center;
		align-items: center;
		.outline-box {
			position: absolute;
			top: 0;
			left: 0;
			bottom: 0;
			z-index: 99;
			align-items: center;
			justify-content: center;
			.outline-img {
				width: 750rpx;
				height: 1125rpx;
			}
		}
		.remind {
			position: absolute;
			top: 880rpx;
			width: 750rpx;
			z-index: 100;
			align-items: center;
			justify-content: center;
			.remind-text {
				color: #dddddd;
				font-weight: bold;
			}
		}
	}
	.menu {
		position: absolute;
		left: 0;
		bottom: 0;
		width: 750rpx;
		height: 180rpx;
		z-index: 98;
		align-items: center;
		justify-content: center;
		.menu-mask {
			position: absolute;
			left: 0;
			bottom: 0;
			width: 750rpx;
			height: 180rpx;
			z-index: 98;
		}
		.menu-back {
			position: absolute;
			left: 30rpx;
			bottom: 50rpx;
			width: 80rpx;
			height: 80rpx;
			z-index: 99;
			align-items: center;
			justify-content: center;
		}
		.menu-snapshot {
			width: 130rpx;
			height: 130rpx;
			z-index: 99;
		}
		.menu-flip {
			position: absolute;
			right: 30rpx;
			bottom: 50rpx;
			width: 80rpx;
			height: 80rpx;
			z-index: 99;
			align-items: center;
			justify-content: center;
		}
	}
}

</style>

因为第一次使用nvue,代码整个都成灰色,需要在vscode设置中按下图配置,代码就会和.vue文件一样变彩色,方便阅读

在realName.vue文件中加入:

<canvas id="canvas-clipper" canvas-id="canvas-clipper" type="2d" :style="{width: canvasSiz.width+'px',height: canvasSiz.height+'px',position: 'absolute',left:'-500000px',top: '-500000px'}" />

这里用到canvas是为了把live-pusher横屏拍摄的身份证裁剪

methods:{
    //设置图片
	setImage(e) {
		console.log('跳回这个页面',e);
        let type = e.dotype == 'face' ? 'front' : 'back'
		//显示在页面
        console.log('type',type);
        // this.upLoadImg(e.path,type)
        this.zjzClipper(e.path,type)
	},
    //证件照裁切
	zjzClipper(path,type) {
		uni.getImageInfo({
			src: path,
			success: (image) => {
				console.log('image',image);
				this.canvasSiz.width =188;
				this.canvasSiz.height =273;
				
				
				//因为nvue页面使用canvas比较麻烦,所以在此页面上处理图片
				let ctx = uni.createCanvasContext('canvas-clipper', this);
				
				ctx.drawImage(
					path,
					parseInt(0.15 * image.width),
					parseInt(0.17 * image.height),
					parseInt(0.69 * image.width),
					parseInt(0.65 * image.height),
					0,
					0,
					188,
					273
				);
				
				ctx.draw(false, () => {
					uni.canvasToTempFilePath(
						{
							destWidth: 188,
							destHeight: 273,
							canvasId: 'canvas-clipper',
							fileType: 'jpg',
							success: (res) => {
								// this.savePhoto(res.tempFilePath);
                                // this.frontImg = res.tempFilePath
                                this.upLoadImg(res.tempFilePath,type)
							}
						},
						this
					);
				});
			}
		});
	},

如果想保存拍摄的照片还可以使用下面方法

savePhoto(path){
		this.imagesrc = path;
		//保存到相册
		uni.saveImageToPhotosAlbum({
			filePath: path,
			success: () => {
				uni.showToast({
					title: '已保存至相册',
					duration: 2000
				});
			}
		});
	},

realName.vue的css样式部分

.personalInfo{
        padding: 27rpx 30rpx;
        box-sizing: border-box;
        background-color: #fff;
        .label{
            display: flex;
            align-items: center;
            margin-bottom: 50rpx;
            .blue{
                width: 8rpx;
                height: 30rpx;
                background-color: #3E71FE;
                margin-right: 12rpx;
            }
            .title{
                font-weight: 500;
                font-size: 32rpx;
                color: #1D1B1A;
            }
        }
        .realName{
            display: flex;
            align-items: center;
            margin-bottom: 30rpx;
            padding-left: 20rpx;
            box-sizing: border-box;
            .name{
                font-weight: 500;
                font-size: 28rpx;
                color: #1D1B1A;
            }
        }
        .tips{
            font-weight: normal;
            font-size: 24rpx;
            color: #929292;
            margin-bottom: 30rpx;
        }
        .lineBottom{
            width: 650rpx;
            height: 2rpx;
            background: #F5F5F5;
            margin: auto;
            margin-bottom: 30rpx;
        }
        .imgBox{
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            padding: 0 82rpx;
            box-sizing: border-box;
            .front{
                position: relative;
                width: 526rpx;
                height: 288rpx;
                border-radius: 20rpx 20rpx 20rpx 20rpx;
                margin-bottom: 28rpx;
                background: url('https://res.qyjpay.cn/static/res/yewuban-smrz-zhengmian.png') no-repeat center;
                background-size: contain;
                .frontBtn{
                    position: absolute;
                    bottom: 50rpx;
                    left: 123rpx;
                    width: 280rpx;
                    height: 90rpx;
                    line-height: 90rpx;
                    background: #3E71FE;
                    box-shadow: 0rpx 8rpx 12rpx 1rpx rgba(62,113,254,0.15);
                    border-radius: 45rpx 45rpx 45rpx 45rpx;
                    font-size: 32rpx;
                    color: #FFFFFF;
                    font-weight: normal;
                    text-align: center;
                }
                image{
                    position: absolute;
                    left: 0;
                    top: 0;
                    width: 526rpx;
                    height: 288rpx;
                    border-radius: 20rpx 20rpx 20rpx 20rpx;
                }
            }
            .transformImg{
                position: absolute;
                left: 120rpx !important;
                top: -120rpx !important;
                transform: rotate(-90deg);
                width: 288rpx !important;
                height: 526rpx !important;
                z-index: 999;
            }
            .back{
                position: relative;
                width: 526rpx;
                height: 288rpx;
                border-radius: 20rpx 20rpx 20rpx 20rpx;
                background: url('https://res.qyjpay.cn/static/res/yewuban-smrz-fanmian.png') no-repeat center;
                background-size: contain;
                .backBtn{
                    position: absolute;
                    bottom: 50rpx;
                    left: 123rpx;
                    width: 280rpx;
                    height: 90rpx;
                    line-height: 90rpx;
                    background: #3E71FE;
                    box-shadow: 0rpx 8rpx 12rpx 1rpx rgba(62,113,254,0.15);
                    border-radius: 45rpx 45rpx 45rpx 45rpx;
                    font-size: 32rpx;
                    color: #FFFFFF;
                    font-weight: normal;
                    text-align: center;
                }
                image{
                    position: absolute;
                    left: 0;
                    top: 0;
                    width: 526rpx;
                    height: 288rpx;
                    border-radius: 20rpx 20rpx 20rpx 20rpx;
                }
            }
        }
    }

里面的图片和结构根据项目需求自行修改

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1619819.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL简解

文章目录 1. MySQL框架2. 执行流程2.1. 连接池&#xff1a;2.2. SQL 前端(SEVER)2.2.0. 查询缓存2.2.1. SQL 接口2.2.2. SQL 解析器2.2.3. SQL 执行器2.2.4. INNODB 中读写操作 2.3. 数据的保存形式 3.其他重要概念3.1. 索引3.1.1. 简单概念3.1.2. 索引优化&#xff1a;1. Usin…

用这个方法,让你轻松从零搭建产品知识库

在市场竞争日益激烈的今天&#xff0c;一个系统化的产品知识库对于企业的重要性不言而喻。它不仅可以帮助团队成员快速掌握产品信息&#xff0c;提升服务效率&#xff0c;还能为客户提供及时准确的产品支持。那么&#xff0c;怎样才能从零开始&#xff0c;轻松搭建起一个合适的…

计算机服务器中了devicdata勒索病毒怎么处理,devicdata勒索病毒解密工具流程

随着网络技术的不断发展与应用&#xff0c;越来越多的企业离不开网络&#xff0c;通过网络可以开展各项工作业务&#xff0c;网络也为企业的生产运营提供各类极大便利&#xff0c;大大提高了生产效率&#xff0c;但网络在为企业提供便利的同时&#xff0c;也为企业的数据安全带…

html网页在展示时,监听网络是否断网,如果断网页面暂停点击响应

序言&#xff1a; 集合百家之所长&#xff0c;方著此篇文章&#xff0c;废话少说&#xff0c;直接上代码&#xff0c;找好你的测试网页&#xff0c;进行配置&#xff0c;然后复制粘贴代码&#xff0c;就可以了。 1.css文件内容 #newbody{display: none;width: 100%;height: 9…

Linux系统-服务器硬件及RAID配置

目录 一.服务器 1.服务器与普通计算机的区别 2.功能 3.分类&#xff08;按照产品形态分&#xff09; 4.架构&#xff08;按照指令集类型&#xff09; 5.相关指令 5.1.查看服务器CPU的信息 5.2.查看服务器内存的信息 二.RAID磁盘阵列&#xff08;Redundant Array …

2024.04.24记录所解决的问题

一解决了使用VM虚拟机作为dubbo-admin服务器提供者在浏览器访问失败问题 一开始在服务器上面即使运行了zookeeper和dubbo-admin容器都没有访问出dubbo-admin的界面管理器。查看了其他映射端口以及注册中心的地址等等其他参数&#xff0c;都不行&#xff0c;然后就一直没有去处…

基于小程序实现的查寝打卡系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;ssm 【…

面试ssss

响应式布局 响应式布局是一种设计和开发网页的方法&#xff0c;使网页能够适应不同的设备和屏幕尺寸&#xff0c;提供更好的用户体验。它通过使用媒体查询&#xff08;Media Queries&#xff09;和弹性布局&#xff08;Flexbox&#xff09;等技术&#xff0c;根据设备的特性和…

【Django】django.core.exceptions.AppRegistryNotReady: Apps aren‘t loaded yet.

其中django后台manage.py入口程序报错&#xff0c;检索很多问题解决方案&#xff0c;这里记录下个人问题原因 1.django启动异常问题详情 django.core.exceptions.AppRegistryNotReady: Apps aren’t loaded yet. 2.问题原因 Python第三方包安装版本不一致或缺少依赖包&…

cdo 修改 calendar 为标准的格式

使用ncl脚本时出现警告&#xff1a;day_of_year: illegal calendar proleptic_gregorian 其原因是读取的降水nc文件是我手动合并生成&#xff0c;所以时间的calendar不是很标准&#xff0c;数据信息如下所示&#xff0c;可以发现Calendar是proleptic_gregorian&#xff0c;这…

互联网盲盒小程序模式,入局市场的渠道选择

近年来&#xff0c;盲盒吸引了无数的消费者&#xff0c;还打造了“万物皆可盲盒”的市场&#xff0c;成为了当下拥有巨大发展前景的行业之一&#xff01;当然&#xff0c;盲盒市场除了吸引消费者外&#xff0c;还获得了众多商家和创业者的青睐。 盲盒作为一种娱乐消费方式&…

C++ | Leetcode C++题解之第42题接雨水

题目&#xff1a; 题解&#xff1a; class Solution { public:int trap(vector<int>& height) {int n height.size();if (n 0) {return 0;}vector<int> leftMax(n);leftMax[0] height[0];for (int i 1; i < n; i) {leftMax[i] max(leftMax[i - 1], he…

linux 守护进程的实现

文章目录 1. 守护进程及实现步骤2. 使用fork 方式创建守护进程3. 使用daemon 函数创建 1. 守护进程及实现步骤 特点&#xff1a; 长期运行&#xff1a;守护进程是一种生存期很长的一种进程&#xff0c;它们一般在系统启动时开始运行&#xff0c;除非强行终止&#xff0c;否则…

《QT实用小工具·三十八》QT炫酷的菜单控件

1、概述 源码放在文章末尾 非常飘逸的 Qt 菜单控件&#xff0c;带有各种动画效果&#xff0c;用起来也十分方便。 无限层级&#xff0c;响应键盘、鼠标单独操作&#xff0c;支持单快捷键。 允许添加自定义 widget、layout&#xff0c;当做特殊的 QDialog 使用。 项目demo演示…

Python写个二维码

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、进入官网下载二、下载一下三.输入代码 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、进入官网下载 官网 pip insta…

Stable Diffusion WebUI 使用 LoRA 调整风格——详细教程

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里&#xff0c;订阅后可阅读专栏内所有文章。 大家好&#xff0c;我是水滴~~ 本教程旨在深入探讨 LoRA 模型的奥秘&#xff0c;涵盖其基本概念、独特作用以及实操指南。我们将从下载和使用LoRA的步…

配线架与交换机:了解差异和最佳用途

什么是配线架&#xff0c;它与交换机有何不同&#xff1f; 配线架是一种具有多个插孔的设备或单元&#xff0c;用于方便管理电缆连接。 它充当静态交换机&#xff0c;允许轻松连接或断开网络设备&#xff0c;并为所有电缆连接提供集中位置。 另一方面&#xff0c;Switch 是一种…

基于深度学习的车牌识别

如果你认为车牌只是车子的‘名字’&#xff0c;那么是时候让你见识一下&#xff0c;当科技赋予它‘超能力’时会发生什么&#xff1f; 上效果图&#xff1b; 这就是车牌识别的力量&#xff0c;下面是主函数代码&#xff1a; # -*- coding: UTF-8 -*- import argparse import …

为底图发愁? 这里有一份清爽又百搭的底图绘制方法!

图纸不够清爽美观&#xff1f; 图纸表达混乱&#xff0c;重点不够醒目&#xff1f; 图纸的颜色太难调了&#xff0c;怎么调都不满意&#xff1f; ...... 俗话说&#xff0c;好的底图是图纸成功的关键&#xff01; 绝大部分的图纸问题&#xff0c;都和底图有关&#xff01; …

【C语言__指针01__复习篇11】

目录 前言 一、什么是指针 二、计算机中常见的单位 三、CPU是怎样找到一块内存空间的 四、如何得到变量的地址 五、指针变量 六、解引用指针变量的作用 七、指针变量的大小 八、指针变量类型的意义 8.1 指针的解引用 8.2 指针-整数 九、void*指针 十、const修饰变…