h5开发中调用摄像头,通常会根据不同的手机显示不同的状态,往往会可以选择相册中的图片,
常规用法:
-input type=file capture

为了让用户必须拍照上传 利用 getUserMedia 实现调用摄像头
以 vue 为例

<template>
  <div class="camera_box" v-if="show">
    <div>
      <div class="video_box">
        <video
          id="videoCamera"
          style="width: 100%; height: 500px"
          playsinline
          autoPlay
        ></video>
      </div>

      <!-- <van-image :src="imgSrc" /> -->
      <canvas id="canvasCamera" v-show="imgShow" />
      <div class="option-btn">
        <Button type="info" @click="cancle">取消</Button>
        <Button type="info" @click="drawImage">拍照</Button>
        <Button type="info" @click="changeCamera">切换摄像头</Button>
      </div>
    </div>
  </div>
</template>

<script>
import { Image, Button, Popup } from "vant";
import Compressor from "compressorjs";
import { lrzUploadImage } from "../../services/upload";
import { IMAGE_LIMIT } from "../../utils/config";

export default {
  props: {
    showCamera: {
      type: Boolean,
      default: false,
    },
    ImgFile: {
      type: Function,
      default: null
    }
  },
  components: {
    Button,
    [Image.name]: Image,
    [Popup.name]: Popup,
  },
  watch: {
    showCamera(newVal) {
      this.show = newVal
      if (newVal) {
        this.getCompetence();
      } else {
        this.stopNavigator();
      }
    },
    show(newVal) {
      this.$emit("update:showCamera", newVal);
    }
  },
  data() {
    return {
      url: "", // 上传的图片的地址
      // visible: false, //弹窗
      videoWidth: 0, // 绘画布的宽高
      videoHeight: 0,
      os: false, //控制摄像头开关
      thisCancas: null,
      thisContext: null,
      thisVideo: null,
      imgSrc: undefined,
      imgFile: null,
      imgShow: false,
      facingMode: "environment", // environment user
      show: false
    };
  },
  methods: {
    changeCamera() {
      this.stopNavigator();
      this.facingMode =
        this.facingMode === "environment" ? "user" : "environment";
      this.getCompetence();
    },
    cancle() {
      this.show = false
      this.$emit("ImgFile", true,'')
    },
    // 调用摄像头权限
    getCompetence() {
      //必须在model中render后才可获取到dom节点,直接获取无法获取到model中的dom节点
      this.$nextTick(() => {
        const _this = this;
        this.os = false; //切换成关闭摄像头
        // 获取画布节点
        this.thisCancas = document.getElementById("canvasCamera");
        // 为画布指定绘画为2d类型
        this.thisContext = this.thisCancas.getContext("2d");
        //获取video节点
        this.thisVideo = document.getElementById("videoCamera");
        // 获取img节点
        this.thisImg = document.getElementById("imgCamera");

        // 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象
        if (navigator.mediaDevices === undefined) {
          //   navigator.menavigatordiaDevices = {};
          navigator.mediaDevices = {};
        }
        console.log("9999", navigator.mediaDevices.getUserMedia);
        // 一些浏览器实现了部分mediaDevices,我们不能只分配一个对象
        // 使用getUserMedia,因为它会覆盖现有的属性。
        // 这里,如果缺少getUserMedia属性,就添加它。
        if (navigator.mediaDevices.getUserMedia === undefined) {
          navigator.mediaDevices.getUserMedia = function (constraints) {
            console.log(
              "navigator",
              navigator.webkitGetUserMedia,
              navigator.mozGetUserMedia,
              navigator.getUserMedia
            );
            // 首先获取现存的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: window.screen.width,
            height: 500,
            // transform: "scaleX(-1)",
            facingMode: this.facingMode,
          },
        };
        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.videoWidth = 500;
             _this.videoHeight = window.screen.width;
            _this.thisCancas.width = 500;
            _this.thisCancas.height = window.screen.width;
 

            _this.thisVideo.onloadedmetadata = function (e) {
              console.log("e", e);
              	// 动态获取视频流宽、高
                 //_this.videoWidth = _this.thisVideo.videoWidth;
	            //_this.videoHeight = _this.thisVideo.videoHeight;
	            //_this.thisCancas.width = _this.thisVideo.videoWidth;
	            //_this.thisCancas.height =_this.thisVideo.videoHeight
              _this.thisVideo.play();
            };
          })
          .catch((err) => {
            console.log("err", err);
          });
      });
    },

    //进行绘制图片
    drawImage() {
      // 点击,canvas画图
      this.thisContext.drawImage(
        this.thisVideo,
        0,
        0,
        this.videoWidth,
        this.videoHeight
      );
      // 获取图片base64链接
      this.imgSrc = this.thisCancas.toDataURL("image/png");
      console.log(this.imgSrc, 8888)
      this.onSubmit()
    },
    //清空画布
    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()?.map((item) => {
          item.stop();
        });
      }
    },
    /*调用摄像头拍照结束*/

    /*完成拍照并对其照片进行上传*/
    onSubmit() {
      this.imgFile = this.dataURLtoFile(this.imgSrc, new Date() + ".png");
      this.stopNavigator();
      this.show = false
      this.beforeRead(this.imgFile).then(res => {
        console.log('压缩结果', res)
        lrzUploadImage(res, (results, url) => {
          console.log('上传结果', results, url)
          this.$emit("ImgFile", results, url)
        });
      })
    
    },

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

    // 图片修正
    beforeRead(file, detail, i = 1) {
      console.log("beforeRead", file);
      // loading
      // this.$emit("beforeRead", this.echoField);
      return new Promise((resolve) => {
        // compressorjs 默认开启 checkOrientation 选项
        let quality = 1;
        let len = IMAGE_LIMIT * (1024 * 1024);
        if (file.size > len) {
          quality = 0.5 ** i;
        }
        // 会将图片修正为正确方向
        new Compressor(file, {
          checkOrientation: true,
          quality: quality,
          success: async (result) => {
            let nowFile = new File([result], result.name, {
              type: file.type,
            });
            if (i >= 2) {
              console.log("2次压缩结束");
              resolve(result);
            } else if (result.size > len) {
              resolve(this.beforeRead(nowFile, detail, Number(i) + 1));
            } else {
              resolve(result);
            }
          },
          error(err) {
            console.log("beforeRead", err);
          },
        });
      });
    },
  },
  mounted() {},
};
</script>


<style lang="less" scoped>
.camera_box {
  width: 100%;
  background: black;
  height: 100vh;
  z-index: 999;
   position: fixed;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
}
.canvas {
  display: none;
}
.option-btn {
  display: flex;
  flex-direction: row;
  justify-content: space-around;
}
.video_box {
  width: 100%;
  height: 500px;
  #videoCamera {
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
  }
}
</style>

动态获取vedio宽高 只能在 onloadedmetadata函数内

getUserMedia 只能在https环境下正确应用, ios只有safari浏览器支持

本地开发可以在本地搭建https环境进行调试

Logo

前往低代码交流专区

更多推荐