UniApp身份证拍照组件

UniApp身份证拍照组件

UniApp 身份证拍照
<template>
  <view class="take-id-card-container">
    <camera
      device-position="back"
      flash="off"
      @error="error"
      class="camera"
      style="width: 100%; height: 100vh; position: absolute"
      v-if="!photoImg"
      @initdone="readyPhone"
    ></camera>
    <view class="take-id-card-photo">
      <view class="tips-top tips mask po-top-0">
        <view class="p-b-20">将身份证人像面对准此区</view>
      </view>
      <view class="tips-bottom tips mask po-bottom-0">
        <view class="p-t-20">拍摄要求:清晰完整,避免缺边、模糊、反光</view>
      </view>
      <view class="photo-area">
        <view class="flex-1 mask"></view>
        <view class="area-content" style="position: relative">
          <image :src="photoImg" v-if="photoImg" />
          <view class="area-border" v-else></view>
          <view class="border po-top-0" style="border-bottom: none; border-right: none"></view>
          <view class="border po-bottom-0" style="border-top: none; border-right: none"></view>
          <view class="border po-bottom-0 po-right-0" style="border-top: none; border-left: none"></view>
          <view class="border po-top-0 po-right-0" style="border-bottom: none; border-left: none"></view>
        </view>
        <view class="flex-1 mask"></view>
      </view>
      <view class="has-photo-btns po-bottom-0" v-if="photoImg">
        <button class="cancel" @click="handleCancel">重拍</button>
        <button class="confirm" @click="handleConfirm">确认使用</button>
      </view>
      <view class="take-photo-btns po-bottom-0 p-b-30" v-else>
        <view style="position: absolute; left: -68px; color: #fff" @click="goBack">取消</view>
        <view class="take-photo" @click="takePhoto">
          <view class="outside"></view>
          <view class="inside"></view>
        </view>
      </view>
    </view>
    <view style="width: 0; height: 0; overflow: hidden">
      <canvas class="canvas" canvas-id="myCanvas" style="width: 330px; height: 210px"></canvas>
    </view>
  </view>
</template>

<script lang="ts">
import { nextTick, reactive, toRefs, getCurrentInstance, defineComponent, onMounted, ref } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'

export default defineComponent({
  name: 'idCard',
  setup() {
    const idCardCameraRef = ref()
    const state = reactive({
      photoImg: '',
      photoWidth: 332,
      photoHeight: 210,
      that: '',
      cameraDisabled: true, // 相机是否可用
      hasPhoneAuth: false // 是否有相机权限
    })

    const takePhoto = () => {
      if (state.cameraDisabled) {
        uni.showToast({
          title: '相机准备中,请稍等',
          icon: 'none'
        })
        return
      }
      if (!state.hasPhoneAuth) {
        uni.showToast({
          title: '尚未进行授权,该功能将无法使用',
          icon: 'none'
        })
        return
      }
      const ctx = uni.createCameraContext()
      ctx.takePhoto({
        quality: 'high',
        success: res => {
          clipImg(res.tempImagePath)
        },
        error(e) {
          console.log(e.detail)
        }
      })
    }
    onMounted(() => {
      authorize()
      state.that = getCurrentInstance()?.proxy
    })

    const authorize = () => {
      uni.getSetting({
        success: res => {
          if (!res.authSetting['scope.camera']) {
            uni.authorize({
              scope: 'scope.camera',
              success: () => {
                console.log('授权成功')
                state.hasPhoneAuth = true
              },
              fail: () => {
                uni.showModal({
                  title: '提示',
                  content: '尚未进行授权,该功能将无法使用',
                  success: res1 => {
                    if (res1.confirm) {
                      uni.openSetting({
                        success: setting => {
                          // console.log(setting);
                          if (setting.authSetting['scope.camera']) {
                            uni.showToast({
                              title: '授权成功!',
                              icon: 'none'
                            })
                            state.hasPhoneAuth = true
                          } else {
                            uni.showToast({
                              title: '授权失败!',
                              icon: 'none'
                            })
                            setTimeout(() => {
                              uni.navigateBack()
                            }, 1000)
                          }
                        }
                      })
                    } else {
                      uni.navigateBack()
                    }
                  }
                })
              }
            })
          }
        }
      })
    }

    // 截取图片
    const clipImg = photoImg => {
      const ctx = uni.createCanvasContext('myCanvas', state.that),
        { photoHeight, photoWidth } = state
      let { windowWidth, windowHeight, devicePixelRatio } = uni.getSystemInfoSync()
      const startX = windowWidth / 2 - photoWidth / 2
      const statrY = windowHeight / 2 - photoHeight / 2 - 80
      ctx.drawImage(photoImg, -startX, -statrY, windowWidth, windowHeight)
      uni.showLoading({
        title: '正在拍摄'
      })
      ctx.draw(
        false,
        (() => {
          setTimeout(() => {
            uni.canvasToTempFilePath(
              {
                canvasId: 'myCanvas',
                width: photoWidth, //分享图片尺寸=画布尺寸1*缩放比0.5*像素比2
                height: photoHeight,
                destHeight: photoHeight,
                destWidth: photoHeight,
                quality: 1,
                fileType: 'jpg',
                success: res => {
                  // 清除画布
                  ctx.clearRect(0, 0, photoWidth, photoHeight)
                  uni.canvasToTempFilePath(
                    {
                      canvasId: 'myCanvas',
                      width: photoWidth, //分享图片尺寸=画布尺寸1*缩放比0.5*像素比2
                      destHeight: photoHeight,
                      destWidth: photoWidth, //分享图片尺寸=画布尺寸1*缩放比0.5*像素比2
                      quality: 1,
                      fileType: 'jpg',
                      success: res => {
                        console.log(res.tempFilePath)
                        state.photoImg = res.tempFilePath
                        uni.hideLoading()
                      }
                    },
                    state.that
                  )
                  // 压缩文件
                  // uni.compressImage({
                  //   src: res.tempFilePath,
                  //   quality: 100,
                  //   success: res => {
                  //     uni.canvasToTempFilePath(
                  //       {
                  //         canvasId: 'myCanvas',
                  //         destWidth: photoWidth, //分享图片尺寸=画布尺寸1*缩放比0.5*像素比2
                  //         destHeight: photoHeight,
                  //         quality: 1,
                  //         fileType: 'jpg',
                  //         success: res => {
                  //           console.log(res.tempFilePath)
                  //           _this.state.photoImg = res.tempFilePath
                  //           uni.hideLoading()
                  //         }
                  //       },
                  //       _this
                  //     )
                  //   }
                  // })
                }
              },
              state.that
            )
          }, 500)
        })()
      )
    }

    const error = () => {
      // 用户不允许使用相机时触发
      uni.showToast({
        title: '尚未进行授权,该功能将无法使用',
        icon: 'none'
      })
    }

    const photoFail = error => {
      console.log('拍照异常', error)
    }

    const handleCancel = () => {
      state.photoImg = ''
    }

    const handleConfirm = () => {
   	// 确认使用函数
    }

    const goBack = () => {
      uni.navigateBack()
    }

    const readyPhone = () => {
      // 相机初始化完成
      state.cameraDisabled = false
    }

    const handleSuccess = (filePath: String) => {
      console.log('身份证照片地址:', filePath)
    }

    return {
      ...toRefs(state),
      idCardCameraRef,
      takePhoto,
      error,
      handleCancel,
      goBack,
      handleConfirm,
      readyPhone,
      photoFail,
      handleSuccess
    }
  }
})
</script>

<style scoped lang="scss">
.take-id-card-container {
  position: relative;
}
.take-id-card-photo {
  font-family: PingFang SC-粗体, PingFang SC;
  position: relative;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  width: 100%;
  position: absolute;
  text-align: center;
  .mask {
    background: rgba(0, 0, 0, 0.5);
  }
  .tips {
    position: absolute;
    width: 100%;
  }
  .tips-top {
    font-size: 36rpx;
    font-weight: normal;
    color: #ffffff;
    line-height: 20px;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    height: calc(50vh - 185px); // 1/2 照片框高度 + 80(bottom高度)
  }
  .tips-bottom {
    font-size: 28rpx;
    font-weight: normal;
    color: #ffffff;
    line-height: 20px;
    height: calc(50vh - 25px); // 1/2 照片框高度 - 80(bottom高度)
  }

  .camera {
    width: 100%;
    height: 100vh;
    position: absolute;
  }
  .photo-area {
    z-index: 2;
    display: flex;
    width: 100%;
    position: absolute;
    top: calc(50vh - 185px);
    left: 50%;
    transform: translateX(-50%);
    image {
      width: 332px;
      height: 210px;
    }
    .area-content {
      width: 332px;
      height: 210px;
    }
    .area-border {
      width: 332px;
      height: 210px;
    }
    .border {
      width: 16px;
      height: 16px;
      border: 3px solid #fd7359;
      position: absolute;
    }
  }
  // .id-card-container {
  //   position: absolute;
  //   border: dotted 2px #ff7272;
  //   top: calc(50vh - 25px);;
  //   left: 50%;
  //   transform: translateX(-50%);
  //   z-index: 2;
  // }
  .has-photo-btns {
    width: 100vw;
    background: #ffffff;
    box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.0397);
    display: flex;
    position: fixed;
    padding: 7px 14px 20px 14px;
    font-size: 16px;
    button {
      line-height: 44px;
      border-radius: 22px;
      padding: 0 54px;
    }
  }
  .cancel {
    background: #ffffff;
    border: 1px solid #dddddd;
    color: #333333;
  }
  .confirm {
    background: #fd7359;
    box-shadow: 0px 4px 8px 0px rgba(253, 115, 89, 0.201);
    color: #ffffff;
  }

  .take-photo-btns {
    position: fixed;
    display: flex;
    justify-content: center;
    align-items: center;
    .take-photo {
      position: relative;
      view {
        border-radius: 50%;
      }
    }
    .outside {
      width: 80px;
      height: 80px;
      background: rgba(255, 255, 255, 0.3);
    }
    .inside {
      width: 64px;
      height: 64px;
      background: #ffffff;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }
  }

  .po-bottom-0 {
    bottom: 0;
  }
  .po-top-0 {
    top: 0;
  }
  .po-left-0 {
    left: 0;
  }
  .po-right-0 {
    right: 0;
  }
  .border-top-none {
    border-top: none;
  }
  .border-bottom-none {
    border-bottom: none;
  }
  .border-left-none {
    border-left: none;
  }
  .border-right-none {
    border-right: none;
  }
  .flex-1 {
    flex: 1;
  }
}
</style>

### UniApp 实现身份证正反面图片上传 在UniApp中实现身份证正反面照片的上传功能涉及多个方面,包括前端页面设计、图片处理以及与服务器端的数据交互。以下是具体方法和示例代码。 #### 页面布局与组件配置 为了方便用户选择并预览所选图片,在`pages/idCardUpload/index.vue`文件内定义如下结构: ```html <template> <view class="container"> <!-- 正面 --> <u-upload :action="uploadUrl" name="frontImage" @on-success="handleSuccess('front')" ref="frontUploader" > <text>点击上传正面</text> </u-upload> <!-- 反面 --> <u-upload :action="uploadUrl" name="backImage" @on-success="handleSuccess('back')" ref="backUploader" > <text>点击上传反面</text> </u-upload> <button type="primary" @click="submit">提交审核</button> </view> </template> ``` 此部分利用了`u-upload`组件来简化图片上传流程,并通过设置不同的`name`属性区分前后两面的照片[^3]。 #### 数据绑定与事件监听 接着是在<script>标签内的逻辑编写: ```javascript export default { data() { return { uploadUrl: 'https://your-server-url/upload', // 替换成实际接口地址 frontImageUrl: '', backImageUrl: '' } }, methods: { handleSuccess(type, res) { if (type === 'front') this.frontImageUrl = res.data.url; else if (type === 'back') this.backImageUrl = res.data.url; uni.showToast({ title: `${type}面上传成功`, icon: 'success' }); }, submit() { const formData = new FormData(); if (!this.frontImageUrl || !this.backImageUrl) { uni.showToast({title:'请先完成全部图片上传'}); return false; } formData.append('front_image_url', this.frontImageUrl); formData.append('back_image_url', this.backImageUrl); // 发送至服务端进一步验证... console.log(formData); // 这里可以发起网络请求发送formData给后台进行下一步操作 } } } ``` 上述代码实现了当每张图片上传完成后更新对应的URL到data对象中;同时提供了一个按钮用于触发最终的信息汇总及提交动作。 #### 图片转换为Base64编码 考虑到某些场景可能需要将本地选取的图片转化为Base64字符串再做后续处理(比如调用第三方API),可以在项目根目录创建一个工具函数库`utils/imageUtil.js`: ```javascript // utils/imageUtil.js function imgToBase64(url, callback){ wx.getImageInfo({ src: url, success(res) { let tempFilePath = res.path; wx.getFileSystemManager().readFile({ filePath: tempFilePath, encoding: 'base64', success(result) { var base64Str = result.data; callback(base64Str); } }) } }) } module.exports = {imgToBase64}; ``` 然后在适当的地方引入该模块并使用其提供的方法即可[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值