H5调用相机进行拍照及切换摄像头及踩坑记录

本文详细介绍了在移动端,尤其是iOS微信内置浏览器中,如何实现摄像头的唤醒、切换及拍照功能。针对iOS无法自动播放video的问题,提出了添加`playsinline`属性的解决方案。同时,当页面跳转后重新调用相机出现黑屏或失败的情况,提出了释放相机资源的解决策略。此外,还分享了将canvas截图转化为图片文件的方法。
摘要由CSDN通过智能技术生成

使用案例:

<div class="ocr-camera" v-show="showCamera">
  <div class="camera-head">
    <span>{{ faceType }}人脸识别</span>
  </div>
  <div class="camera" id="camera">
    <video style="width: 100%; height: 100%; object-fit: cover" id="video" autoplay="autoplay" playsinline></video>
    <div class="mask-view">
      <div>请将正脸移入框内</div>
    </div>
  </div>
  <div class="camera-button">
    <span style="font-size: 15px" @click="cancel">取消</span>
    <svg class="icon" aria-hidden="true" style="height: 65px; width: 65px" @click="takePicture">
      <use xlink:href="#icon-paizhao-copy"></use>
    </svg>
    <svg class="icon" aria-hidden="true" style="height: 40px; width: 40px" @click="switchCamera">
      <use xlink:href="#icon-qiehuanshexiangtou-copy"></use>
    </svg>
  </div>
  <!--描绘video截图-->
  <canvas v-show="showPicture" id="canvas" width="300" height="300" style="position: fixed; left: 10000px"></canvas>
</div>
//mounted中
	this.video = document.getElementById("video");
    this.canvas = document.getElementById("canvas");
    this.context = this.canvas.getContext("2d");
// 老的浏览器可能根本没有实现 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;

        // 一些浏览器根本没实现它 - 那么就返回一个error到promise的reject来保持一个统一的接口
        if (!getUserMedia) {
          return Promise.reject(
            new Error("getUserMedia is not implemented in this browser")
          );
        }

        // 否则,为老的navigator.getUserMedia方法包裹一个Promise
        return new Promise(function (resolve, reject) {
          getUserMedia.call(navigator, constraints, resolve, reject);
        });
      };
    }
//methods中:
 //开始拍照
    checkFace(typeName,type,index) {
      //默认使用前摄像头,强制使用后置摄像头如下设置
      // let constraints = {video: { facingMode: { exact: "environment" } }};
      if (this.stream) {
        this.stream.getTracks().forEach((track) => {
          track.stop();
        });
      }
      let constraints = { video: { facingMode: "user" } };
      navigator.mediaDevices
        .getUserMedia(constraints)
        .then((stream) => {
          this.stream = stream;
          // 旧的浏览器可能没有srcObject
          if ("srcObject" in this.video) {
            this.video.srcObject = stream;
            this.video.setAttribute('playsinline', true)
					  this.video.play()
          } else {
            // 防止在新的浏览器里使用它,应为它已经不再支持了
            this.video.src = window.URL.createObjectURL(stream);
            this.video.setAttribute('playsinline', true)
					  this.video.play()
          }
          this.video.onloadedmetadata = (e) => {
            this.video.play();
          };
        })
        .catch(function (err) {
          console.log(err.name + ": " + err.message);
        });
      this.showCamera = true;
    },
    //切换前后置
    switchCamera() {
      let constraints = { video: { facingMode: "user" } };
      if (this.isFrontCamera) {
        constraints = {
          video: {
            facingMode: { exact: "environment" },
          },
        };
      }
      if (this.stream) {
        this.stream.getTracks().forEach((track) => {
          track.stop();
        });
      }

      navigator.mediaDevices
        .getUserMedia(constraints)
        .then((stream) => {
          this.stream = stream;
          this.isFrontCamera = !this.isFrontCamera;
          // 旧的浏览器可能没有srcObject
          if ("srcObject" in this.video) {
            this.video.srcObject = stream;
            this.video.setAttribute('playsinline', true)
			this.video.play()
          } else {
            // 防止在新的浏览器里使用它,应为它已经不再支持了
            this.video.src = window.URL.createObjectURL(stream);
            this.video.setAttribute('playsinline', true)
					  this.video.play()
          }
          this.video.onloadedmetadata = (e) => {
            this.video.play();
          };
        })
        .catch(function (err) {
          alert(err.name + ": " + err.message);
          console.log(err.name + ": " + err.message);
        });
    },
    takePicture() {//点击拍摄按钮,并生成照片文件
      console.log(this.video.videoWidth, this.video.videoHeight);
      this.canvas.width = this.video.videoWidth;
      this.canvas.height = this.video.videoHeight;
      //绘制画面
      this.context.drawImage(
        this.video,
        0,
        0,
        this.canvas.width,
        this.canvas.height
      );
      // canvas 转图片,再base64转流
      let blob = baseTofile(this.canvas.toDataURL("image/png"));
      function blobtoFile({ type } = blob) {
        console.log(type, "type");
        return new File([blob], "人脸照片文件", { type });
      }
      this.showCamera = false;
      this.showDialog = true;
      //上传结果,掉接口识别,等待结果返回后,去掉弹框
      this.idNamePhotoCheck(blobtoFile(blob))
      
    },
    // base64格式转文件流
	export function baseTofile(base64) {
	  var bytes = window.atob(base64.split(",")[1]); //去掉url的头,并转换为byte
	  //处理异常,将ascii码小于0的转换为大于0
	  var ab = new ArrayBuffer(bytes.length);
	  var ia = new Uint8Array(ab);
	  for (var i = 0; i < bytes.length; i++) {
	    ia[i] = bytes.charCodeAt(i);
	  }
	  return new Blob([ab], { type: "image/png" });
	}

踩坑记录1:
在浏览器中调用是正常的,但是在ios系统的微信内置浏览器中调用时,摄像头就唤醒不成功,原因:ios不会自动播放video

解决方案:
添加如下代码即可

//这个属性是ios微信浏览器设置可以让视频在小窗口内播放,也就是不是全屏播放
this.video.setAttribute('playsinline', true)
//播放
this.video.play()

踩坑记录2:
当在A页面调用相机前置摄像头拍照后,通过路由跳转至B页面,此时再次调用相机后置摄像头黑屏或失败 ,原因:当前设备被占用

解决方案:
在A页面销毁时,如果相机设备被占用,就将它释放掉
在每次调用相机前,先关闭相机设备

destroyed(){
    if (this.stream) {
        this.stream.getTracks().forEach((track) => {
          track.stop();
        });
      }
  },
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值