Vue调用PC摄像头实现拍照功能

编辑: admin 分类: javascript 发布时间: 2021-11-17 来源:互联网

本文实例为大家分享了Vue调用PC摄像头实现拍照功能的具体代码,供大家参考,具体内容如下

项目需求:可以本地上传头像,也可以选择拍摄头像上传。

组件:

1、Camera组件:实现 打开、关闭摄像头、绘制、显示图片、用于上传
2、CameraDialog组件:使用ElementUI dialog组件 展示摄像头UI效果
3、外部调用CameraDialog组件,实现拍摄头像上传功能
4、本地上传可使用原生input、也可使用ElementUI upload组件

操作逻辑:

1、新增时将头像图片转为base64调用接口提交,返回url地址用于前端展示
2、替换时,先执行删除操作,在依新增操作执行。
3、本地上传原理跟拍摄上传一致

具体实现方法:

Camera组件

<template>
  <div class="camera-box">
    <video id="video" :width="videoWidth" :height="videoHeight" v-show="!imgSrc"></video>
    <canvas id="canvas" :width="videoWidth" :height="videoHeight" v-show="imgSrc"></canvas>
    <p class="camera-p">{{!imgSrc?'提示:请将头像居中按"拍照"键确认':''}}</p>
    <el-button type="primary" @click="setImage" v-if="!imgSrc" class="camera-btn">拍照</el-button>
    <el-button type="primary" v-if="imgSrc" @click="setFileUpload" class="camera-btn">上传</el-button>
  </div>
</template>

<script>
  import {setFileUpload, deleteFileUpload, addUserCard } from "@/api/houseApi";

  export default {
    name: 'Camera',
    props: {
      //【必选】CameraDialog弹窗显示状态
      show: {type: Boolean},
      //【可选】配合原生input本地上传,用于替换时执行删除
      deleteData: {type: Object}
    },
    data() {
      return {
        videoWidth: '401',
        videoHeight: '340',
        thisCancas: null,
        thisContext: null,
        thisVideo: null,
        imgSrc: ``,
      }
    },
    mounted() {
      if (this.show) this.getCompetence()
    },
    methods: {
      /*
       *@author Brady
       *@Time 2019/9/5
       *@function  调用权限
       *****************************************/
      getCompetence() {
        var _this = this
        this.thisCancas = document.getElementById('canvas')
        this.thisContext = this.thisCancas.getContext('2d')
        this.thisVideo = document.getElementById('video')
        // 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象
        if (navigator.mediaDevices === undefined) {
          navigator.mediaDevices = {}
        }
        // 一些浏览器实现了部分mediaDevices,我们不能只分配一个对象
        // 使用getUserMedia,因为它会覆盖现有的属性。
        // 这里,如果缺少getUserMedia属性,就添加它。
        if (navigator.mediaDevices.getUserMedia === undefined) {
          navigator.mediaDevices.getUserMedia = function (constraints) {
            // 首先获取现存的getUserMedia(如果存在)
            var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.getUserMedia
            // 有些浏览器不支持,会返回错误信息
            // 保持接口一致
            if (!getUserMedia) {
              return Promise.reject(new Error('getUserMedia is not implemented in this browser'))
            }
            // 否则,使用Promise将调用包装到旧的navigator.getUserMedia
            return new Promise(function (resolve, reject) {
              getUserMedia.call(navigator, constraints, resolve, reject)
            })
          }
        }
        var constraints = {
          audio: false,
          video: {width: this.videoWidth, height: this.videoHeight, transform: 'scaleX(-1)'}
        }
        navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
          // 旧的浏览器可能没有srcObject
          if ('srcObject' in _this.thisVideo) {
            _this.thisVideo.srcObject = stream
          } else {
            // 避免在新的浏览器中使用它,因为它正在被弃用。
            _this.thisVideo.src = window.URL.createObjectURL(stream)
          }
          _this.thisVideo.onloadedmetadata = function (e) {
            _this.thisVideo.play()
          }
        }).catch(err => {
          console.log(err)
        })
      },
      /*
       *@author Brady
       *@Time 2019/9/5
       *@function  绘制图片
       *****************************************/
      setImage() {
        var _this = this
        // 点击,canvas画图
        _this.thisContext.drawImage(_this.thisVideo, 0, 0, _this.videoWidth, _this.videoHeight)
        // 获取图片base64链接
        var image = this.thisCancas.toDataURL('image/png')
        _this.imgSrc = image
        // console.log(_this.imgSrc)
        // this.$emit('refreshDataList', this.imgSrc)
      },
      /*
       *@author Brady
       *@Time 2019/9/5
       *@function  base64转文件
       *****************************************/
      dataURLtoFile(dataurl, filename) {
        var arr = dataurl.split(',')
        var mime = arr[0].match(/:(.*?);/)[1]
        var bstr = atob(arr[1])
        var n = bstr.length
        var u8arr = new Uint8Array(n)
        while (n--) {
          u8arr[n] = bstr.charCodeAt(n)
        }
        return new File([u8arr], filename, {type: mime})
      },
      /*
       *@author Brady
       *@Time 2019/9/5
       *@function  关闭摄像头
       *****************************************/
      stopNavigator() {
        this.thisVideo.srcObject.getTracks()[0].stop()
      },
      //上传图片
      setFileUpload() {
        //编辑档案-上传人脸照片
        if(this.deleteData) {
          if (this.deleteData.imagePath) {
            deleteFileUpload({id: this.deleteData.id, filePath: this.deleteData.imagePath})
              .then(res => {
                setFileUpload({image: this.imgSrc})
                  .then(res => {
                    this.$emit('fileUpload', res.retData.filePath, res.retData.imagePath)
                    addUserCard({userId: this.deleteData.userid, cardType: this.deleteData.cardType, userAuditInfo: res.retData.imagePath})
                      .then(res => {
                        this.$message({message: "上传成功", type: "success"})
                      })
                      .catch(err => {
                        console.log(err)
                      })
                  })
                  .catch(err => {
                    console.log(err)
                  })
              })
              .catch(err => {
                console.log(err)
              })
          } else {
            setFileUpload({image: this.imgSrc})
              .then(res => {
                this.$emit('fileUpload', res.retData.filePath, res.retData.imagePath)
                addUserCard({userId: this.deleteData.userid, cardType: this.deleteData.cardType, userAuditInfo: res.retData.imagePath})
                  .then(res => {
                    this.$message({message: "上传成功", type: "success"})
                  })
                  .catch(err => {
                    console.log(err)
                  })
              })
              .catch(err => {
                console.log(err)
              })
          }
        } else {
          //添加住户-上传人脸照片
          setFileUpload({image: this.imgSrc})
            .then(res => {
              // console.log(res)
              this.$message({message: "上传成功", type: "success"})
              this.$emit('fileUpload', res.retData.filePath, res.retData.imagePath)
            })
            .catch(err => {
              console.log(err)
            })
        }
      },
    },
    watch: {
      show(val) {
        if (val) {
          this.imgSrc = ``
          this.getCompetence()
        } else {
          this.stopNavigator()
        }
      },
    }
  }
</script>

<style lang="less">
  .camera-box {
    margin: 0 auto;
    text-align: center;

    .camera-p {
      height: 17px;
      line-height: 17px;
      font-size: 12px;
      font-family: "PingFang SC";
      font-weight: 400;
      color: rgba(154, 154, 154, 1);
      text-align: left;
    }

    .camera-btn {
      margin-top: 20px;
    }

  }
</style>

CameraDialog组件

<template>
  <div id="camera-dialog">
    <el-dialog
            title="拍摄照片"
            :visible.sync="dialogVisible"
            top="5vh"
            width="481px"
            @close="dialogCancle"
            :close-on-click-modal="false"
            :before-close="dialogCancle"
    >
      <Camera :show="dialogVisible" :deleteData="deleteData" @fileUpload="fileUpload"></Camera>
      <span slot="footer" class="dialog-footer">
          <!-- <el-button @click="dialogCancle">取 消</el-button> 国内服务器http://www.558idc.com/yz.html-->
        <!-- <el-button type="primary">确 定</el-button> -->
        </span>
    </el-dialog>
  </div>
</template>

<script>
  import Camera from "@/page/house/Camera.vue"

  export default {
    name: 'CameraDialog',
    props: {
      dialogVisible: {type: Boolean},
      deleteData: {type: Object}
    },
    components: {
      Camera
    },
    data() {
      return {
        filePath: ``,
        imagePath: ``,
      }
    },
    methods: {
      //关闭弹窗
      dialogCancle() {
        this.$emit('dialogCancle', false, this.filePath, this.imagePath);
      },
      //获取人脸照片地址
      fileUpload(filePath, imagePath) {
        this.filePath = filePath
        this.imagePath = imagePath
        this.dialogCancle()
      }
    }
  }
</script>

<style scoped>
</style>

外部调用组件

<template>
  <div>
    <div class="form-thumb">
     <img :src="filePath" alt="">
      <i class="delete-btn" @click="deleteUploadFile" v-if="deleteData.imagePath">x</i>
    </div>
    <div class="upload-btn">
       <input type="file" name="userAuditInfo" id="userAuditInfo" @change="getUploadFile" ref="inputFile">
       <el-button type="defualt" size="small" @click="localUploadFile">本地上传</el-button>
       <el-button type="default" size="small" @click="dialogVisible=true">拍摄照片</el-button>
     </div>
    <!-- 拍摄照片弹窗 -->
    <CameraDialog :dialogVisible="dialogVisible" @dialogCancle="dialogCancleCamera" :deleteData="deleteData" />
  </div> 
</template>

<script>
  import CameraDialog from "./CameraDialog.vue"
  import { setFileUpload, deleteFileUpload, addUserCard } from "@/api/houseApi.js"
  export default {
    data() {
      return {
        filePath: require('@/assets/images/null.png'), //身份证头像
        dialogVisible: false,
        //操作删除人脸照片相关字段
        deleteData: {
          userid: this.$route.query.userId,
          id: ``,
          cardType: 4,
          imagePath: ``,
        }
   }
 },
 methods: {
      //模拟点击本地上传人脸照片
      localUploadFile() {
        this.$refs.inputFile.click()
      },
      //本地上传人脸照片
      getUploadFile() {
        let input = document.getElementById('userAuditInfo')
        let file = input.files[0]
        this.getBase64(file)
          .then(res => {
            if (this.deleteData.imagePath) {
              deleteFileUpload({id: this.deleteData.id, filePath: this.deleteData.imagePath})
                .then(() => {
                  this.setFileUpload(res)
                })
            } else {
              this.setFileUpload(res)
            }
          })
          .catch(err => {
            console.log(err)
          })
      },
      //上传人脸照片
      setFileUpload(res) {
        setFileUpload({image: res})
          .then(res => {
            this.filePath = res.retData.filePath
            this.deleteData.imagePath = res.retData.imagePath
            addUserCard({userId: this.deleteData.userid, cardType: this.deleteData.cardType, userAuditInfo: res.retData.imagePath})
              .then(res => {
                this.$message({message: res.retInfo, type: "success"})
                //用于更新数据,此方法未展示
                this.getInfo()
              })
              .catch(err => {
                console.log(err)
              })
          })
          .catch(err => {
            console.log(err)
          })
      },
      //转base64
      getBase64(file) {
        return new Promise(function (resolve, reject) {
          let reader = new FileReader();
          let imgResult = "";
          reader.readAsDataURL(file);
          reader.onload = function () {
            imgResult = reader.result;
          };
          reader.onerror = function (error) {
            reject(error);
          };
          reader.onloadend = function () {
            resolve(imgResult);
          };
        });
      },
      //删除人脸照片
      deleteUploadFile() {
        this.$confirm(`确认删除?`, '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          deleteFileUpload({id: this.deleteData.id, filePath: this.deleteData.imagePath})
            .then(res => {
              this.$message({message: res.retInfo, type: "success"})
              this.filePath = require('@/assets/images/null.png')
              this.deleteData.imagePath = ''
            })
            .catch(err => {
              console.log(err)
            })
        }).catch(() => {});
      },
      //Dialog弹窗取消、获取上传人脸照片
      dialogCancleCamera(str, filePath, imagePath) {
        this.dialogVisible = str
        // this.houseInfo.filePath = filePath
        // this.houseInfo.userAuditInfo = imagePath
        this.filePath = filePath
        this.deleteData.imagePath = imagePath
        this.getInfo()
      }, 
 }
  }
</script> 

<style scoped="scoped">
  .upload-btn {
    position: relative;
    margin: 20px 12px 0 0;
    text-align: right;
  }
  input#userAuditInfo {
    position: absolute;
    display: inline-block;
    width: 80px;
    height: 32px;
    top: 0;
    cursor: pointer;
    font-size: 0;
    z-index: -1;
    /*opacity: 0;*/
  }
  .delete-btn {
    position: absolute;
    top: -6px;
    right: -6px;
    display: inline-block;
    width: 16px;
    height: 16px;
    line-height: 14px;
    background: rgba(251, 135, 66, 1);
    border-radius: 8px;
    text-align: center;
    font-size: 12px;
    color: #fff;
    cursor: pointer;
  }
</style>

以上只作为实现参考,具体操作依实际需求做相应调整。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持hwidc。