vue2项目实现扫描条形码和二维码功能

说明:不支持http环境,仅支持https和本地localhost环境

使用 @zxing/library 库实现

  • 支持条形码二维码
  • 安装
  • npm install --save webrtc-adapter
  • npm install --save @zxing/library
  • 完整代码
<template>
  <div class="page-scan" style="flex: 1;position: relative;">
    <!-- 扫码区域 -->
    <div class="QrCode" style="height: 100%;">
      <video ref="video" height="100%" id="video" autoplay object-fit="cover"></video>
    </div>
    <!-- 扫码样式 -->
    <div class="qr-scanner">
      <div class="box">
        <div class="line"></div>
        <div class="angle"></div>
      </div>
      <!-- 遮罩层 实现周围暗 中间亮的效果 -->
      <div class="mask1"></div>
      <div class="mask2"></div>
      <div class="mask3"></div>
      <div class="mask4"></div>
    </div>
  </div>
</template>

<script>
// WebRTC适配器 只需要引入就ok
import 'webrtc-adapter'
import { BrowserMultiFormatReader } from '@zxing/library'
export default {
  name: 'scanCodePage',
  data() {
    return {
      codeReader: null
    }
  },
  mounted() {
    this.codeReader = new BrowserMultiFormatReader()
    this.openScan()
  },
  beforeUnmount() {
    this.codeReader && this.codeReader.reset()
  },
  methods: {
    async openScan() {
      this.codeReader
        .listVideoInputDevices()
        .then((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.codeReader && this.codeReader.reset() // 重置
          this.decodeFromInputVideoFunc(firstDeviceId)
        })
        .catch((err) => {
          this.$toast.fail(err)
        })
    },
    decodeFromInputVideoFunc(firstDeviceId) {
      this.codeReader.decodeFromInputVideoDeviceContinuously(
        firstDeviceId,
        'video',
        (result, err) => {
          if (result) {
            this.$toast.success(`扫描结果:${result}`)
          }
          if (err && !err) {
            this.$toast.fail(err)
          }
        }
      )
    }
  }
}
</script>

<style lang="scss" scope>
.qr-scanner {
  background-size: 3rem 3rem;
  background-position: -1rem -1rem;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 9;
  width: 100%;
  height: 100%;
}

.qr-scanner .box {
  width: 75vw;
  height: 75vw;
  max-height: 75vh;
  max-width: 75vh;
  position: relative;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  overflow: hidden;
}

.qr-scanner .line {
  height: calc(100% - 2px);
  width: 100%;
  background: linear-gradient(180deg, rgba(0, 255, 51, 0) 43%, #3F90FD 211%);
  border-bottom: 3px solid #3F90FD;
  transform: translateY(-100%);
  animation: radar-beam 2s infinite;
  animation-timing-function: cubic-bezier(0.53, 0, 0.43, 0.99);
  animation-delay: 1.4s;
}

.qr-scanner .box:after,
.qr-scanner .box:before,
.qr-scanner .angle:after,
.qr-scanner .angle:before {
  content: '';
  display: block;
  position: absolute;
  width: 3vw;
  height: 3vw;
  border: 0.2rem solid transparent;
}

.qr-scanner .box:after,
.qr-scanner .box:before {
  top: 0;
  border-top-color: #3F90FD;
}

.qr-scanner .angle:after,
.qr-scanner .angle:before {
  bottom: 0;
  border-bottom-color: #3F90FD;
}

.qr-scanner .box:before,
.qr-scanner .angle:before {
  left: 0;
  border-left-color: #3F90FD;
}

.qr-scanner .box:after,
.qr-scanner .angle:after {
  right: 0;
  border-right-color: #3F90FD;
}

@keyframes radar-beam {
  0% {
    transform: translateY(-100%);
  }

  100% {
    transform: translateY(0);
  }
}

.qr-scanner .back-arrow {
  position: fixed;
  top: 20px;
  left: 20px;
  width: 30px;
  height: 30px;
  border-radius: 100%;
  background-color: rgba(0, 0, 0, 0.3);
  z-index: 999;

  display: flex;
  justify-content: center;
  align-items: center;
  color: #fff;
}

.qr-scanner .mask1 {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  // top right bottom left
  clip-path: inset(0 0 calc(50% + (75vw / 2)) 0);
}

.qr-scanner .mask2 {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  clip-path: inset(calc(50% - (75vw / 2)) calc(50% + (75vw / 2)) 0 0);
}

.qr-scanner .mask3 {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  clip-path: inset(calc(50% + (75vw / 2)) 0 0 calc(50% - (75vw / 2)));
}

.qr-scanner .mask4 {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  clip-path: inset(calc(50% - (75vw / 2)) 0 calc(50% - (75vw / 2)) calc(50% + (75vw / 2)));
}
</style>

  • 效果视频

vue项目扫描条形码和二维码效果

使用 vue-qrcode-reader 库实现

  • 仅支持二维码,支持打开手机闪光灯
  • 安装
  • npm install vue-qrcode-reader
  • 完整代码
<template>
  <div class="scanner">
    <qrcode-stream :torch="torchActive" @decode="onDecode" @init="onInit" style="height: 100%; width:100vw"
      :decoder-config="{ formats: ['code_128', 'ean_8', 'ean_13', 'code_39', 'itf', 'codabar'] }">
      <div class="qr-scanner">
        <div class="box">
          <div class="line"></div>
          <div class="angle"></div>
          <div @click="openTorch" class="openTorch center">轻触点亮</div>
        </div>
        <!-- 遮罩层 实现周围暗 中间亮的效果 -->
        <div class="mask1"></div>
        <div class="mask2"></div>
        <div class="mask3"></div>
        <div class="mask4"></div>
      </div>
    </qrcode-stream>
  </div>
</template>
<script>
import { QrcodeStream } from 'vue-qrcode-reader'

export default {
  props: {
    operationType: {
      type: String,
      default: '',
    },
  },
  components: {
    QrcodeStream
  },
  data() {
    return {
      result: '', // 扫码结果信息
      torchActive: false,
      error: '' // 错误信息
    }
  },
  methods: {
    // 打开手电筒
    openTorch() {
      this.torchActive = !this.torchActive
    },
    onDecode(result) {
      if (result) {
        this.torchActive = false
        this.$toast.success(`扫描结果:${result}`)
      } else {
        this.$toast('未获得扫码结果!')
      }
    },
    async onInit(promise) {
      try {
        await promise
      } catch (error) {
        this.$toast.fail(error)
        if (error.name === 'NotAllowedError') {
          alert('您需要授予相机访问权限')
        } else if (error.name === 'NotFoundError') {
          alert('这个设备上没有摄像头')
        } else if (error.name === 'NotSupportedError') {
          this.$toast.fail('所需的安全上下文(HTTPS、本地主机)')
        } else if (error.name === 'NotReadableError') {
          this.$toast.fail('相机被占用')
        } else if (error.name === 'OverconstrainedError') {
        } else if (error.name === 'StreamApiNotSupportedError') {
          this.$toast.fail('此浏览器不支持流API')
        } else if (error.name === 'InsecureContextErroe') {
          this.$toast.fail('请在安全的情况下才访问摄像头')
        }
      }
    },
  }
}
</script>
<style lang="less" scoped>
.scanner {
  width: 100vw;
  height: 500px;
}

.qr-scanner {
  width: 100%;
  height: 100%;
  position: relative;
}

.qr-scanner .box {
  width: 260px;
  height: 260px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  overflow: hidden;
  z-index: 999;
}

.qr-scanner .line {
  height: calc(100% - 2px);
  width: 100%;
  background: linear-gradient(180deg,
      rgba(0, 255, 51, 0) 43%,
      #1989fa 211%);
  border-bottom: 3px solid #1989fa;
  transform: translateY(-100%);
  animation: radar-beam 2s infinite alternate;
  animation-timing-function: cubic-bezier(0.53, 0, 0.43, 0.99);
  animation-delay: 1.4s;
}

.qr-scanner .box:after,
.qr-scanner .box:before,
.qr-scanner .angle:after,
.qr-scanner .angle:before {
  content: '';
  display: block;
  position: absolute;
  width: 6vw;
  height: 6vw;
  border: 5px solid transparent;
}

.qr-scanner .box:after,
.qr-scanner .box:before {
  top: 0;
  border-top-color: #3F90FD;
}

.qr-scanner .angle:after,
.qr-scanner .angle:before {
  bottom: 0;
  border-bottom-color: #3F90FD;
}

.qr-scanner .box:before,
.qr-scanner .angle:before {
  left: 0;
  border-left-color: #3F90FD;
}

.qr-scanner .box:after,
.qr-scanner .angle:after {
  right: 0;
  border-right-color: #3F90FD;
}

.openTorch {
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  font-size: 20px;
  color: #fff;
}

.qr-scanner .mask1 {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  // top right bottom left
  clip-path: inset(0 0 calc(50% + 130px) 0);   
}
.qr-scanner .mask2 {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  clip-path: inset(calc(50% - 130px) calc(50% + 130px) 0 0);
}
.qr-scanner .mask3 {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  clip-path: inset(calc(50% + 130px) 0 0 calc(50% - 130px));
}
.qr-scanner .mask4 {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  clip-path: inset(calc(50% - 130px) 0 calc(50% - 130px) calc(50% + 130px));
}

@keyframes radar-beam {
  0% {
    transform: translateY(-100%);
  }

  100% {
    transform: translateY(0);
  }
}
</style>    
  • 效果视频

vue项目扫描二维码效果

你可能感兴趣的:(前端,vue.js,二维码,条形码)