vue2封装摄像头拍照(输出流文件)

该文章展示了如何在Vue.js中创建一个基于baseDialog组件的拍照功能。组件包括视频流显示、canvas截图、图片预览和保存。利用navigator.mediaDevices.getUserMedia获取摄像头权限,并通过canvas将视频帧转换为图片。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于baseDialog组件做的拍照弹窗组件,baseDialog组件请看本人的上一篇文章懒人BseDialog

<template>
  <base-dialog ref="dialog"
               width="900px"
               type="form"
               :fullscreen="true"
               :title="title"
               footer
               :before-close="close"
               @closed="closed">
    <template slot="body">
      <div class="cameraBox">
        <div class="public-mask mask-grey-bg">
          <div class="mask-main camera-main">
            <div class="mask-title">
            </div>
            <div class="camera-box">
              <div class="camera-left">
                <!-- 这里就是摄像头显示的画面 -->
                <video id="videoCamera"
                       width="400px"
                       height="400px"></video>
              </div>
              <div class="camera-right">
                <!-- 这里是点击拍照显示的图片画面 -->
                <img v-if="imgSrc"
                     :src="imgSrc"
                     style="width: 100%;height:;" />
                <span v-if="!imgSrc">暂无照片</span>
                <canvas id="canvasCamera"
                        class="canvas"
                        :width='videoWidth'
                        :height='videoHeight'
                        style="display: none;"></canvas>
              </div>
            </div>
          </div>
        </div>
      </div>
    </template>
    <template slot="footer">
      <el-button type="primary"
                 @click="drawImage">拍照</el-button>
      <el-button type="primary"
                 @click="save">保存</el-button>
    </template>
  </base-dialog>
</template>
<script>
import { dataURLtoBlob, blobToFile } from '@/utils/file.js'
import BaseDialog from '@/components/Dialog.vue'
export default {
  name: "CameraDialog",
  components: {
    BaseDialog
  },
  data () {
    return {
      title: '',
      /** 照相机弹窗模块-start */
      // cameraOpen: false,
      imgSrc: undefined,
      os: false,//控制摄像头开关
      thisVideo: null,
      thisContext: null,
      thisCancas: null,
      videoWidth: 800,
      videoHeight: 600,
      /** 照相机弹窗模块-end */
    }
  },
  computed: {
  },
  created () { },
  methods: {
    /** 调用摄像头拍照-start*/
    // 打开照相机弹窗
    open () {
      this.$refs.dialog.open()
      this.title = '人脸采集'
      this.getCompetence()
    },
    // 调用摄像头权限
    getCompetence () {
      //必须在model中render后才可获取到dom节点,直接获取无法获取到model中的dom节点
      this.$nextTick(() => {
        const _this = this;
        this.os = false;//切换成关闭摄像头
        this.thisCancas = document.getElementById('canvasCamera');//这里是需要截取的canvas的Id名称
        this.thisContext = this.thisCancas.getContext('2d');
        this.thisVideo = document.getElementById('videoCamera');
        // 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象
        if (navigator.mediaDevices === undefined) {
          navigator.menavigatordiaDevices = {}
        }
        // 一些浏览器实现了部分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 () {
      console.log(this.thisCancas.toDataURL())
      // 点击,canvas画图
      this.thisContext.drawImage(this.thisVideo, 0, 0, this.videoWidth, this.videoHeight);
      // 获取图片base64链接,展示到界面中的也是这个url地址
      this.imgSrc = this.thisCancas.toDataURL('image/png');
    },
    //清空画布
    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.os = true;//切换成打开摄像头
      }
    },
    /** 调用摄像头拍照-end*/
    close () {
      console.log(456)
      this.resetCanvas(); // 重置画布
      this.stopNavigator(); // 关闭摄像头
      this.$refs.dialog.close()
    },
    closed () { },
    // 上传照相机图片
    save () {
      let blobData = dataURLtoBlob(this.imgSrc)
      let fileData = blobToFile(blobData)
      console.log(fileData)
    }
  }
}
</script>
<style lang='scss' scoped>
.camera-box {
  display: flex;
  align-items: center;
  justify-content: space-around;
  .camera-left {
    background: #000;
    margin-right: 20px;
    width: 400px;
    height: 400px;
  }
  .camera-right {
    width: 400px;
    height: 400px;
    box-sizing: border-box;
    border: 3px dashed #ccc;
    display: flex;
    background: #000;
    align-items: center;
    justify-content: space-around;
    span {
      display: inline-block;
      text-align: center;
      color: #fff;
      font-size: 30px;
      font-weight: 500;
    }
  }
}
</style>

拍照组件中外部引入的base64转换File的方法如下

//将base64转换为blob
export function dataURLtoBlob (dataurl) {
  var arr = dataurl.split(','),
    mime = arr[0].match(/:(.*?);/)[1],
    bstr = window.atob(arr[1]),
    n = bstr.length,
    u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], { type: mime });
}
//将blob转换为file
export function blobToFile (theBlob, fileName) {
  theBlob.lastModifiedDate = new Date()
  theBlob.name = fileName;
  return theBlob;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值