getUserMedia了解 Link to heading

HTML5的getUserMedia API为用户提供访问硬件设备媒体(摄像头、麦克风)的接口,基于该接口,开发者可以在不依赖任何浏览器插件的条件下访问硬件媒体设备。 点击查看getUserMedia的api

代码 Link to heading

Vue.js版

HTML Link to heading

<template>
  <div id="scanner">
    <div class="model">
      <div class="scanner-view">
        <div class="scanner-view-arrow arrow1"></div>
        <div class="scanner-view-arrow arrow2"></div>
        <div class="scanner-view-arrow arrow3"></div>
        <div class="scanner-view-arrow arrow4"></div>
        <div class="scanner-line"></div>
      </div>
    </div>
    <video class="video-view" ref="video" autoplay playsinline="true" webkit-playsinline="true"></video>
    <canvas ref="canvas" width="478" height="850" style="display: none"></canvas>
  </div>
</template>

JS Link to heading

<script>
  import jsQR from "jsqr";
  import Quagga from "quagga";
  export default {
    name: '',
    data() {
      return {
        cameraWidth: 0,
        cameraHeight: 0
      }
    },
    methods: {
      initVideo(constrains){
        let _this = this;
        if(navigator.mediaDevices.getUserMedia){
          //最新标准API
          navigator.mediaDevices.getUserMedia(constrains).then(_this.videoSuccess).catch(_this.videoError);
        } else if (navigator.webkitGetUserMedia){
          //webkit内核浏览器
          navigator.webkitGetUserMedia(constrains).then(_this.videoSuccess).catch(_this.videoError);
        } else if (navigator.mozGetUserMedia){
          //Firefox浏览器
          navagator.mozGetUserMedia(constrains).then(_this.videoSuccess).catch(_this.videoError);
        } else if (navigator.getUserMedia){
          //旧版API
          navigator.getUserMedia(constrains).then(_this.videoSuccess).catch(_this.videoError);
        }
      },
      videoSuccess(stream){
        let video = this.$refs.video,
            _this = this;
        //将视频流设置为video元素的源
        video.srcObject = stream;
        //播放视频
        video.play();

        video.oncanplay = function () {
          // 摄像头分辨率,手机480x640
          console.log('摄像头分辨率');
          console.log(video.videoWidth,video.videoHeight);
          _this.cameraWidth = video.videoWidth;
          _this.cameraHeight = video.videoHeight;
          // 发送图片进行识别
          _this.readImg();
        };
      },
      videoError(error){
        console.log("访问用户媒体设备失败:",error.name,error.message);
      },
      readImg(){
        let video = this.$refs.video,
            canvas = this.$refs.canvas,
            context = canvas.getContext("2d"),
            _this = this;
        let timer = setInterval(function () {
          context.drawImage(video,0,0,_this.cameraWidth,_this.cameraHeight,0,0,478,850);
          // 扫码条形码
          let imgUri = canvas.toDataURL();
          _this.readBarcode(imgUri,timer);

          // 扫码二维码
          let imageData = context.getImageData(0, 0, 478, 850);
          _this.readQrcode(imageData.data,timer);
        },1000)
      },
      readBarcode(imgBase64,timer){
        let _this = this;
        Quagga.decodeSingle({
          inputStream: {
            size: 1920
          },
          locator: {
            patchSize: "medium",
            halfSample: false
          },
          decoder: {
            readers: [{
              format: "code_128_reader",
              config: {}
            }]
          },
          locate: true,
          src: imgBase64
        }, function(result){
          if (result){
            if(result.codeResult) {
              console.log(result.codeResult);
              clearInterval(timer);
              _this.$emit('ondata',result.codeResult.code);
//              alert("扫码成功,结果是..."+result.codeResult.code);

            } else {
              console.log("正在扫条形码...not detected");
            }
          }else {
            console.log("正在扫条形码...not detected");
          }

        });
      },
      readQrcode(data,timer){
        let _this = this;
        let code = jsQR(data, 478, 850, {
          inversionAttempts: "dontInvert",
        });

        if (code){
          clearInterval(timer);
          _this.$emit('ondata',code.data);
//          alert('扫码成功,结果是...' + code.data);
        }else {
          console.log('正在扫二维码...');
        }
      }
    },
    mounted(){
      if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia){
        //调用用户媒体设备,访问摄像头
        this.initVideo({
          video:{
            height: 800,
            facingMode: {
              // 强制后置摄像头
              exact: "environment"
            }
          }
        });
      } else {
        alert("你的浏览器不支持访问用户媒体设备");
      }
    }
  }
</script>

CSS Link to heading

<style scoped>
  #scanner {
    font-family: 'Avenir', Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    position: relative;
  }
  .model{
    box-sizing: border-box;
    width: 100vw;
    height: 100vh;
    position: relative;
    z-index: 88;
    border-top: calc((100vh - 60vw)/2) solid rgba(0,0,0,.2);
    border-bottom: calc((100vh - 60vw)/2) solid rgba(0,0,0,.2);
    border-right: 20vw solid rgba(0,0,0,.2);
    border-left: 20vw solid rgba(0,0,0,.2);
  }
  .scanner-view{
    width: 100%;
    height: 100%;
    position: relative;
    border: 1px solid rgba(255,255,255,.3);
    z-index: 89;
  }
  .scanner-line{
    position: absolute;
    width: 100%;
    height: 1px;
    background: #49FF46;
    border-radius: 20px;
    z-index: 90;
    animation: myScan 1s infinite alternate;
  }
  @keyframes  myScan{
    from {
      top: 0;
    }
    to {
      top: 34vh;
    }
  }
  .scanner-view-arrow{
    position: absolute;
    width: 5vw;
    height: 5vw;
    border: 2px solid #09bb07;
  }
  .scanner-view-arrow.arrow1{
    top: -1px;
    left: 0px;
    z-index: 99;
    border-right: none;
    border-bottom: none;
  }
  .scanner-view-arrow.arrow2{
    top: -1px;
    right: 0px;
    z-index: 99;
    border-left: none;
    border-bottom: none;
  }
  .scanner-view-arrow.arrow3{
    bottom: -1px;
    left: 0px;
    z-index: 99;
    border-right: none;
    border-top: none;
  }
  .scanner-view-arrow.arrow4{
    bottom: -1px;
    right: 0px;
    z-index: 99;
    border-left: none;
    border-top: none;
  }
  .video-view{
    position: absolute;
    width: 100vw;
    height: 100vh;
    object-fit: cover;
    top: 0px;
    left: 0px;
    z-index: 80;
  }
</style>

注意事项 Link to heading

  • video标签里面的视频会用黑边,可以在video标签的css中加入 object-fit: cover;

  • ios中会出现点击视频全屏情况,可以在video标签中加入 playsinline="true" webkit-playsinline="true"

  • 示例里面用了ElementUI的弹窗组件