vue - web 调用摄像头进行拍照

List.vue

<template>
  <!--人像采集-->
  <div class="typt-list-container">
    <div>人像采集</div>
    <img v-if="image" :src="image" class="border" style="width: 200px;height: 200px;"/>
    <el-button @click="openCamera">打开摄像头</el-button>
    <el-dialog :visible.sync="cameraVisible"
               class="passport-imgDialog"
               :before-close="closeCamera"
               width="800px"
               :show-close="true"
               style="padding: 0;"
               title="人像采集"
               :close-on-click-modal="false">
      <div style="height: 400px;text-align: center;" v-if="cameraHack">
        <Photograph :image="image"
                    :statusInfo="statusInfo"
                    @getPhoto="getPhoto"></Photograph>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import gateCamera from '../../../assets/acms/mixins/gateCamera/index.js'
import Photograph from '../../../components/acms/Photograph'
import { MsgShow } from '../../../assets/acms/js/Message'
export default {
  components: {
    Photograph
  },
  mixins: [gateCamera],
  data () {
    return {
      cameraVisible: false,
      cameraHack: false,
      image: '', // 传递图像数据
      statusInfo: { // 状态显示
        status: '', // loading | fail
        msg: ''
      }
    }
  },
  props: ['params', 'modifyData', 'pageData', 'searchData'],
  mounted () {
  },
  methods: {
    // 获取拍照图像
    getPhoto ({ formdata, base64 }) {
      MsgShow('success', '获取中......', 2000)
      // 上传中
      this.statusInfo.status = 'loading'
      this.statusInfo.msg = '上传中'
      // 上传成功
      setTimeout(() => {
        MsgShow('success', '人像采集成功', 2000)
        this.statusInfo.status = ''
        this.statusInfo.msg = ''
        this.image = base64
        this.closeCamera()
      }, 2000)
    },
    // 打开摄像头
    openCamera () {
      this.cameraHack = false
      this.cameraVisible = false
      this.$nextTick(() => {
        this.cameraHack = true
        this.cameraVisible = true
      })
    },
    // 关闭拍照弹窗
    closeCamera () {
      this.cameraHack = false
      this.cameraVisible = false
    }
  }
}
</script>

Photograph.vue

<template>
    <div id="Photograph">
        <div class="ptoContainer">
        <video id="video" class="video" width="320" autoplay="autoplay"></video>
        <div :style="{backgroundImage: `url(${image})`}"
             class="headImage"
             v-if="image"
             v-show="!cameraStatus"></div>
        <!-- 图片获取状态 -->
        <div class="photoViewStatus" v-show="photoAlertStatus || statusInfo.status !== ''">
        <div class="Loading">
<!--        <img src="./loading.png"-->
        <img src="/static/acms/img/default.png"
             class="loader"
             v-show="statusInfo.status === 'loading'"/>
<!--        <img src="./success.png"-->
        <img src="/static/acms/img/default.png"
             class="loaderNoAni"
             v-show="statusInfo.status === 'success'"/>
        </div>
        <p>{{statusInfo.msg}}</p>
    </div>
    </div>
        <div class="btnBox">
        <!-- 拍照按钮 -->
        <button class="btn" v-if="!cameraStatus" @click="connectCameraFn">拍照</button>
        <button class="btn" v-else @click="takePhotoFn">确认</button>
    </div>
    </div>
</template>
<script>
import MyLib from './MyLib.js'
import { ResultMsg } from '../../../assets/acms/js/Message'
export default {
  name: '',
  props: {
    image: {
      type: String,
      default: ''
    },
    statusInfo: {
      type: Object,
      // eslint-disable-next-line vue/require-valid-default-prop
      default: {
        status: '', // loading | success
        msg: ''
      }
    }
  },
  data () {
    return {
      cameraStatus: false, // 拍照状态, true => 开启;false => 关闭
      photo: '',
      photoAlertStatus: false, // 图像提示文案显示状态 true => 显示;false => 隐藏
      getStatus: true // true => loading状态, false => 失败状态
    }
  },
  computed: {
    // 浏览器类型/版本(控制摄像头video区域大小),在部分浏览器获取的摄像头拍照区域为长方形,所以需要判断浏览器,当前低版本的 safari 和 uc 会是长方形区域
    systemCameraVideo () {
      let system = MyLib.judgeBrowser()
      let v = parseInt(system.version.replace(/\./g, ''))
      if (system.browser === 'chrome' || (system.browser === 'safari' && v >= 1211)) {
        return true
      } else {
        return false
      }
    }
  },
  methods: {
    // 连接摄像头
    connectCameraFn () {
      this.photoAlertStatus = false // 关闭提示框
      // 区域控制
      let obj = {}
      if (this.systemCameraVideo) {
        obj = { width: 320, height: 320 }
      } else {
        obj = { width: 320 }
      }
      // end
      let constraints = {
        video: obj,
        audio: false
      }
      const video = document.getElementById('video')
      // 当前在IE浏览器无法调用摄像头
      try {
        let Promise = navigator.mediaDevices.getUserMedia(constraints)
        Promise.then(MediaStream => {
          video.srcObject = MediaStream
          window.MediaStreamTrack = MediaStream.getTracks()[0] // 摄像头对象
          video.play()
          this.cameraStatus = true
        })
      } catch (err) {
        ResultMsg('error', '浏览器不支持调用摄像头,请更换最新的谷歌浏览器,或者其他最新版')
      }
    },
    // 绘制照片
    takePhotoFn () {
      let video = document.getElementById('video')
      let canvas = document.createElement('canvas')
      canvas.width = canvas.height = 320
      let ctx = canvas.getContext('2d')
      let w = video.offsetWidth
      let h = video.offsetHeight
      let y = (canvas.width - h) / 2
      // 区域控制
      if (this.systemCameraVideo) {
        y = 0
      }
      // end
      ctx.drawImage(video, 0, y, w, h)
      let base64 = canvas.toDataURL('image/jpeg', 0.4)
      this.photo = base64
      // base64 => FormData
      let Blob = MyLib.convertBase64UrlToBlob(base64)
      let formData = new FormData()
      formData.append('file', Blob)
      this.$emit('getPhoto', { formData: this.photo, base64: base64 })
      console.log(base64)
      // 关闭
      this.cameraStatus = false // 修改摄像头启动状态
      window.MediaStreamTrack &&
                window.MediaStreamTrack.stop &&
                window.MediaStreamTrack.stop() // 关闭摄像头
      this.deviceInfoStatus = false
      // end
    }
  }
}
</script>

MyLib.js

const MyLib = {
  // base64转blob
  convertBase64UrlToBlob (urlData) {
    let arr = urlData.split(',')
    let mime = arr[0].match(/:(.*?);/)[1]
    let bstr = atob(arr[1])
    let n = bstr.length
    let u8arr = new Uint8Array(n)
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n)
    }
    return new Blob([u8arr], { type: mime })
  },
  // 浏览器类型判断及版本
  judgeBrowser () {
    // 获取IE浏览器版本
    function getIeVersion () {
      let IEMode = document.documentMode
      let rMsie = /(msie\s|trident.*rv:)([\w.]+)/
      let ma = navigator.userAgent.toLowerCase()
      let match = rMsie.exec(ma)
      try {
        return match[2]
      } catch (e) {
        return IEMode
      }
    }
    let System = {}
    let ua = navigator.userAgent.toLowerCase()
    if (ua.match(/msie/) != null || ua.match(/trident/) != null) {
      System.browser = 'IE'
      System.version = getIeVersion()
    } else if (ua.indexOf('ubrowser') > -1) {
      System.browser = 'uc'
      System.version = ''
    } else {
      let re = /(msie|firefox|chrome|opera|version).*?([\d.]+)/
      let m = ua.match(re)
      System.browser = m[1].replace(/version/, 'safari')
      System.version = m[2]
    }
    return System
  }
}
export default MyLib
返回文章列表 打赏
本页链接的二维码
打赏二维码
期待你的评论