浏览器扫一扫功能

浏览器扫一扫功能,PC端已测试成功,移动端需要https没测试
1.html5-qrcode,安装 npm i html5-qrcode

<template>
  <div style="height: 100%; width: 100%">
    <MyHeader :name="'调用摄像头扫码'" left="arrow-left" @goBackEv="$emit('goBack')" />
    <div class="qrcode">
      <div id="reader"></div>
    </div>
  </div>
</template>

<script>
import {
    
     Html5Qrcode } from "html5-qrcode"
export default {
    
    
  components: {
    
     Html5Qrcode },
  data() {
    
    
    return {
    
    
      html5QrCode: null
    }
  },
  created() {
    
    
    this.getCameras()
  },
  beforeDestroy() {
    
    
    if (this.html5QrCode) this.stop()
  },
  methods: {
    
    
    getCameras() {
    
    
      Html5Qrcode.getCameras()
        .then((devices) => {
    
    
          if (devices && devices.length) {
    
    
            this.html5QrCode = new Html5Qrcode("reader")
            this.start()
          }
        })
        .catch((err) => {
    
    
          // handle err
          this.html5QrCode = new Html5Qrcode("reader")
          this.tools.errorPrompt('您需要授予相机访问权限')
        })
    },
    start() {
    
    
      this.html5QrCode
        .start(
       	  // environment后置摄像头 user前置摄像头
          {
    
     facingMode: "environment" },
          {
    
    
            fps: 2, // 可选,每秒帧扫描二维码
            qrbox: {
    
     width: 250, height: 250 }, // 可选,如果你想要有界框UI
            // aspectRatio: 1.777778 // 可选,视频馈送需要的纵横比,(4:3--1.333334, 16:9--1.777778, 1:1--1.0)传递错误的纵横比会导致视频不显示
          },
          (decodedText, decodedResult) => {
    
    
            // do something when code is read
            console.log('decodedText', decodedText);
            console.log('decodedResult', decodedResult);
            this.$emit("goBack", decodedText)
          }
        )
        .catch((err) => {
    
    
          console.log('扫码错误信息', err);
          // 错误信息处理仅供参考,具体情况看输出!!!
          if (typeof err == 'string') {
    
    
            this.tools.errorPrompt(err)
          } else {
    
    
            if (err.name == 'NotAllowedError') return this.tools.errorPrompt("您需要授予相机访问权限")
            if (err.name == 'NotFoundError') return this.tools.errorPrompt('这个设备上没有摄像头')
            if (err.name == 'NotSupportedError') return this.tools.errorPrompt('摄像头访问只支持在安全的上下文中,如https或localhost')
            if (err.name == 'NotReadableError') return this.tools.errorPrompt('相机被占用')
            if (err.name == 'OverconstrainedError') return this.tools.errorPrompt('安装摄像头不合适')
            if (err.name == 'StreamApiNotSupportedError') return this.tools.errorPrompt('此浏览器不支持流API')
          }
        })
    },
    stop() {
    
    
      this.html5QrCode.stop().then((ignore) => {
    
    
        // QR Code scanning is stopped.
        console.log("QR Code scanning stopped.")
      })
        .catch((err) => {
    
    
          // Stop failed, handle it.
          console.log("Unable to stop scanning.")
        })
    },
  }
}
</script>
 
<style lang="less" scoped>
.qrcode {
    
    
  position: relative;
  height: 100%;
  width: 100%;
  background-color: rgba(0, 0, 0, 0.1);
}
#reader {
    
    
  top: 20%;
  left: 0;
}
</style>

2.vue-qrcode-reader 安装 npm install --save vue-qecode-reader

<template>
  <div style="height: 100%; width: 100%">
    <MyHeader :name="'扫码'" left="arrow-left" @goBackEv="$emit('goBack')" />
    <qrcode-stream
      :key="_uid"
      :track="this.paintBoundingBox"
      @decode="onDecode"
      @init="onInit"
    /> 
  </div>
</template>

<script>
import {
    
     QrcodeStream } from 'vue-qrcode-reader'
export default {
    
    
  components: {
    
     QrcodeStream },
  name:'qrCodeReader',
  methods: {
    
    
    onDecode(result) {
    
    
      console.log('扫码结果', result)
      this.$emit('goBack', result)
    },
    async onInit(promise) {
    
    
      try {
    
    
        await promise
      } catch (error) {
    
    
        if (error.name === 'NotAllowedError') {
    
    
          // ERROR: you need to grant camera access permission
          this.tools.errorPrompt("您需要授予摄像机访问权限")
        } else if (error.name === 'NotFoundError') {
    
    
          // ERROR: no camera on this device
          this.tools.errorPrompt("这个设备上没有摄像头")
        } else if (error.name === 'NotSupportedError') {
    
    
          // ERROR: secure context required (HTTPS, localhost)
          this.tools.errorPrompt("需要安全上下文(HTTPS, localhost)")
        } else if (error.name === 'NotReadableError') {
    
    
          // ERROR: is the camera already in use?
          this.tools.errorPrompt("摄像机已经在使用了吗?")
        } else if (error.name === 'OverconstrainedError') {
    
    
          // ERROR: installed cameras are not suitable
          this.tools.errorPrompt("安装的摄像头不合适")
        } else if (error.name === 'StreamApiNotSupportedError') {
    
    
          // ERROR: Stream API is not supported in this browser
          this.tools.errorPrompt("此浏览器不支持流API")
        } else if (error.name === 'InsecureContextError') {
    
    
          // ERROR: Camera access is only permitted in secure context. 
          // Use HTTPS or localhost rather than HTTP.
          this.tools.errorPrompt(`摄像头只能在安全环境下使用。使用HTTPS或本地主机,而不是HTTP`)
        } else {
    
    
          this.tools.errorPrompt(`ERROR: Camera error (${
      
      error.name})`)
        }
      }
    },
    /** 追踪二维码的样式
	* 注意: 避免访问这个函数中的变量、计算属性以及vuex,该函数是每秒调用,会造成内存泄漏
	* detectedCodes:位置对象
	* ctx:CanvasRenderingContext2D实例
   	*/
    paintBoundingBox(detectedCodes, ctx) {
    
    
      for (const detectedCode of detectedCodes) {
    
    
        const {
    
     boundingBox: {
    
     x, y, width, height } } = detectedCode
        ctx.lineWidth = 2
        ctx.strokeStyle = '#007bff'
        ctx.strokeRect(x, y, width, height)
      }
    }
  }
}
</script>
 
<style>
</style>

3.@zxing/library 安装npm i @zxing/library --save

<template>
  <div class="page-scan">
    <!--返回-->
    <van-nav-bar title="扫描二维码/条形码" fixed
      @click-left="clickIndexLeft()"
      class="scan-index-bar">
      <template #left>
        <van-icon name="arrow-left" size="18" color="#fff"/>
        <span style="color: #fff"> 取消 </span>
      </template>
    </van-nav-bar>
    <!-- 扫码区域 -->
    <video ref="video" id="video" class="scan-video" autoplay></video>
    <!-- 提示语 -->
    <div v-show="tipShow" class="scan-tip"> {
    
    {
    
    tipMsg}} </div>
  </div>
</template>

<script>
import {
    
     BrowserMultiFormatReader } from '@zxing/library';

  export default {
    
    
    name: 'scanCodePage',
    data() {
    
    
      return {
    
    
        loadingShow: false,
        codeReader: null,
        scanText: '',
        vin: null,
        tipMsg: '正在尝试识别....',
        tipShow: false
      }
    },
    created() {
    
    
      this.codeReader = new BrowserMultiFormatReader();
        if (window.stream) {
    
    
            window.stream.getTracks().forEach(track => {
    
    
                track.stop();
            });
        }
      this.openScan();
    },
    destroyed(){
    
    
      this.codeReader.reset();
    },
    watch: {
    
    
      '$route'(to, from) {
    
    
        if(to.path == '/scanCodePage'){
    
    
          this.codeReader = new BrowserMultiFormatReader();
            if (window.stream) {
    
    
                window.stream.getTracks().forEach(track => {
    
    
                    track.stop();
                });
            }
          this.openScanTwo();
        }
      }
    },
    methods: {
    
    
      async openScan() {
    
    
        this.codeReader.getVideoInputDevices().then((videoInputDevices) => {
    
    
          this.tipShow = true;
          this.tipMsg = '正在调用摄像头...';
          console.log('videoInputDevices', videoInputDevices);
          // 默认获取第一个摄像头设备id
          let firstDeviceId = videoInputDevices[0].deviceId;
          // 获取第一个摄像头设备的名称
          const videoInputDeviceslablestr = JSON.stringify(videoInputDevices[0].label);
          if (videoInputDevices.length > 1) {
    
    
            // 判断是否后置摄像头
            if (videoInputDeviceslablestr.indexOf('back') > -1) {
    
    
              firstDeviceId = videoInputDevices[0].deviceId;
            } else {
    
    
              firstDeviceId = videoInputDevices[1].deviceId;
            }
          }
          this.tools.errorPrompt(0+firstDeviceId);
          this.decodeFromInputVideoFunc(firstDeviceId);
        }).catch(err => {
    
    
          this.tipShow = false;
          this.tools.errorPrompt(0+err);
        });
      },
      async openScanTwo() {
    
    
        this.codeReader = await new BrowserMultiFormatReader();
        this.codeReader.getVideoInputDevices().then((videoInputDevices) => {
    
    
          this.tipShow = true;
          this.tipMsg = '正在调用摄像头...';
          this.tools.errorPrompt('videoInputDevices', videoInputDevices);
          // 默认获取第一个摄像头设备id
          let firstDeviceId = videoInputDevices[0].deviceId;
          // 获取第一个摄像头设备的名称
          const videoInputDeviceslablestr = JSON.stringify(videoInputDevices[0].label);
          if (videoInputDevices.length > 1) {
    
    
            // 判断是否后置摄像头
            if (videoInputDeviceslablestr.indexOf('back') > -1) {
    
    
              firstDeviceId = videoInputDevices[0].deviceId;
            } else {
    
    
              firstDeviceId = videoInputDevices[1].deviceId;
            }
          }
          this.decodeFromInputVideoFunc(firstDeviceId);
        }).catch(err => {
    
    
          this.tipShow = false;
          this.tools.errorPrompt(1+err);
        });
      },
      decodeFromInputVideoFunc(firstDeviceId) {
    
    
        this.codeReader.reset(); // 重置
        this.scanText = '';
        this.codeReader.decodeFromInputVideoDeviceContinuously(firstDeviceId, 'video', (result, err) => {
    
    
          this.tipMsg = '正在尝试识别...';
          this.scanText = '';
          if (result) {
    
    
            console.log('扫描结果', result);
            this.scanText = result.text;
            if (this.scanText) {
    
    
              this.tipShow = false;
              this.$emit("getScanResult", this.scanText);
              // 这部分接下去的代码根据需要,读者自行编写了
              // this.$store.commit('app/SET_SCANTEXT', result.text);
              // console.log('已扫描的小票列表', this.$store.getters.scanTextArr);
            }
          }
          if (err && !(err)) {
    
    
            this.tipMsg = '识别失败';
            setTimeout(() => {
    
    
              this.tipShow = false;
            }, 2000)
            this.tools.errorPrompt(err);
          }
        });
      },
      clickIndexLeft(){
    
      // 返回上一页
        this.codeReader = null;
        this.$destroy();
        this.$router.back();
      }
    }
  }
</script>

<style lang="scss">
.scan-index-bar{
    
    
  background-image: linear-gradient( -45deg, #42a5ff ,#59cfff);
}
.van-nav-bar__title{
    
    
  color: #fff !important;
}
.scan-video{
    
    
  height: 80vh;
}
.scan-tip{
    
    
  width: 100vw;
  text-align: center;
  margin-bottom: 10vh;
  color: white;
  font-size: 5vw;
}
.page-scan{
    
    
  overflow-y: hidden;
  background-color: #363636;
}
</style>

猜你喜欢

转载自blog.csdn.net/weixin_44812604/article/details/129524964
今日推荐