Vue調用PC攝像頭實現拍照功能

本文實例為大傢分享瞭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> -->
        <!-- <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>

以上隻作為實現參考,具體操作依實際需求做相應調整。

以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。

推薦閱讀: