vue+elementUI实现调用PC摄像头拍照上传图片(谷歌火狐测试可用,其他自测)

一定部署到https域名上,否则无法调用摄像头权限!!!

效果图

定义Data

 data () {
      return {
        dialogTableVisible:false,
        videoWidth: 500,
        videoHeight: 400,
        imgSrc: '',
        thisCancas: null,
        thisContext: null,
        thisVideo: null,
        ImgFile:'',//返回的图片文件
      }
    },
调用摄像头权限
getCompetence () {
        this.$nextTick(function () {
          var _this = this;
          this.thisCancas = document.getElementById('canvasCamera');
          this.thisContext = this.thisCancas.getContext('2d');
          this.thisVideo = document.getElementById('videoCamera');
          // 旧版本浏览器可能根本不支持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)
          })
        });
      },

绘制图片,生成一个base64文件

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;
      },

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 })
      },

上传文件流至后台(form提交)

handleUpdata(){//已form提交
        if (this.imgSrc!==''){
          let file = this.imgSrc; // 把整个base64给file
          let type = "image/jpeg"; // 定义图片类型(canvas转的图片一般都是png,也可以指定其他类型)
          let time=(new Date()).valueOf();//生成时间戳
          let name = time + ".jpg"; // 定义文件名字(例如:abc.png , cover.png)
          let conversions = this.dataURLtoFile(file, name); // 调用base64转图片方法
          let parms=new FormData();
          parms.append('file',conversions);
          let url='*********';//你的接口
          this.axios.post(url,parms,{headers: {'Content-Type': 'multipart/form-data'}}).then(res=>{
            console.log(res);
            this.ImgFile=res.data;
          }).catch(err=>{
            this.$notify.error({
              title: '上传失败',
              message: err.msg
            });
          })
        }
      },

关闭摄像头

stopNavigator () {
        this.thisVideo.srcObject.getTracks()[0].stop()
      },
 

离开页面时关闭摄像头

 beforeDestroy () {
      this.stopNavigator()
    }

附上源码:

<template>
  <div>
    <el-button
      @click="onTake"
      icon="el-icon-camera"
      size="small">
      拍照上传
    </el-button>
    <el-dialog
      title="拍照上传"
      :visible.sync="visible"
      @close="onCancel"
      width="1065px">
      <div class="box">
        <video id="videoCamera" class="canvas" :width="videoWidth" :height="videoHeight" autoPlay></video>
        <canvas id="canvasCamera" class="canvas" :width="videoWidth" :height="videoHeight"></canvas>
      </div>
      <div slot="footer">
        <el-button
          @click="drawImage"
          icon="el-icon-camera"
          size="small">
          拍照
        </el-button>
        <el-button
          v-if="open"
          @click="getCompetence"
          icon="el-icon-video-camera"
          size="small">
          打开摄像头
        </el-button>
        <el-button
          v-else
          @click="stopNavigator"
          icon="el-icon-switch-button"
          size="small">
          关闭摄像头
        </el-button>
        <el-button
          @click="resetCanvas"
          icon="el-icon-refresh"
          size="small">
          重置
        </el-button>
        <el-button
          @click="onCancel"
          icon="el-icon-circle-close"
          size="small">
          取消
        </el-button>
        <el-button
          @click="onUpload"
          :loading="loading"
          type="primary"
          icon="el-icon-upload2"
          size="small">
          上传
        </el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
  //拍照上传组件
  //父组件通过函数 getImg 获取照片路径,如 @getImg="getImg"
  const Address = require('../utils/url');//图片上传地址
  export default {
    name: "TakePhotos",
    data() {
      return {
        imgSrc: "",
        visible: false,//弹窗
        loading: false,//上传按钮加载
        open: false,//控制摄像头开关
        thisVideo: null,
        thisContext: null,
        thisCancas: null,
        videoWidth: 500,
        videoHeight: 400
      }
    },
    methods: {
      onTake() {
        this.visible = true;
        this.getCompetence();
      },
      onCancel() {
        this.visible = false;
        this.resetCanvas();
        this.stopNavigator();
      },
      //base64转成文件后上传
      onUpload() {
        if (this.imgSrc) {
          const file = this.imgSrc; // 把整个base64给file
          const time = (new Date()).valueOf();//生成时间戳
          const name = time + ".png"; // 定义文件名字(例如:abc.png , cover.png)
          const conversions = this.dataURLtoFile(file, name); // 调用base64转图片方法
          const data = new FormData();
          data.append('file', conversions);
          const options = {
            method: "POST",   //请求方法
            body: data,   //请求体
            headers: {
              'Accept': 'application/json'
            },
          };
          this.loading = true;
          fetch(Address.UPLOAD, options)
            .then((response) => {
              return response.json();
            })
            .then((responseText) => {
              this.loading = false;
              if (responseText.code === 0) {
                this.imgSrc = responseText.data.src;
                this.$emit('getImg', responseText.data.src);//传递给父组件
                this.onCancel();
                this.$notify({
                  title: '上传成功',
                  message: responseText.msg,
                  type: 'success'
                });
              }
            })
            .catch((error) => {
              this.loading = false;
              this.$notify.error({
                title: '上传失败',
                message: error.msg,
              });
            })
        }
        else {
          this.$notify({
            title: '警告',
            message: '请点击拍照',
            type: 'warning'
          });
        }
      },
      // 调用摄像头权限
      getCompetence() {
        //必须在model中render后才可获取到dom节点,直接获取无法获取到model中的dom节点
        this.$nextTick(() => {
          const _this = this;
          this.open = false;//切换成关闭摄像头
          this.thisCancas = document.getElementById('canvasCamera');
          this.thisContext = this.thisCancas.getContext('2d');
          this.thisVideo = document.getElementById('videoCamera');
          // 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象
          if (navigator.mediaDevices === undefined) {
            navigator.mediaDevices = {}
          }
          // 一些浏览器实现了部分mediaDevices,我们不能只分配一个对象
          // 使用getUserMedia,因为它会覆盖现有的属性。
          // 这里,如果缺少getUserMedia属性,就添加它。
          if (navigator.mediaDevices.getUserMedia === undefined) {
            navigator.mediaDevices.getUserMedia = function (constraints) {
              // 首先获取现存的getUserMedia(如果存在)
              let 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)
              })
            }
          }
          const 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 => {
            this.$notify({
              title: '警告',
              message: '没有开启摄像头权限或浏览器版本不兼容.',
              type: 'warning'
            });
          });
        });
      },
      //绘制图片
      drawImage() {
        // 点击,canvas画图
        this.thisContext.drawImage(this.thisVideo, 0, 0, this.videoWidth, this.videoHeight);
        // 获取图片base64链接
        this.imgSrc = this.thisCancas.toDataURL('image/png');
      },
      //base64转文件
      dataURLtoFile(dataurl, filename) {
        let arr = dataurl.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 File([u8arr], filename, {type: mime})
      },
      //清空画布
      clearCanvas(id) {
        let c = document.getElementById(id);
        let cxt = c.getContext("2d");
        cxt.clearRect(0, 0, c.width, c.height);
      },
      //重置画布
      resetCanvas() {
        this.imgSrc = "";
        this.clearCanvas('canvasCamera');
      },
      //关闭摄像头
      stopNavigator() {
        if (this.thisVideo && this.thisVideo !== null) {
          this.thisVideo.srcObject.getTracks()[0].stop();
          this.open = true;//切换成打开摄像头
        }
      },
    },
    beforeDestroy() {
      this.stopNavigator()
    }
  }
</script>

<style scoped>
  .box {
    display: flex;
    justify-content: space-between;
  }

  .canvas {
    border: 1px solid #e8e8e8;
  }
</style>

 

  • 8
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值