基本上同时兼容安卓和苹果的插件都需要付费,这里我找了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;
}
}
}
}
里面的图片和结构根据项目需求自行修改