1. 下载与安装插件vue-qecode-reader
官网:https://gruhn.github.io/vue-qrcode-reader/demos/Simple.html
npm install --save vue-qecode-reader
或者 cnpm install --save vue-qrcode-reader
2.实现
1.QrcodeReader.vue
camera 有着以下属性:
rear: 当camera为rear值的时候调用前摄像头;
front: 当camera为front值的时候调用后摄像头;
off: 当camera为off值的时候调用关闭摄像头,会获取最后一帧用于显示;
auto: 是用于开始获取摄像头,也是默认属性。
torch 的属性是布尔值,当为
true的时候打开手电筒,
false是关闭摄像头。
<template> <div class="qrcode"> <div class="code"> <div class="qrcode-drop-zone" v-show="qrcode"> <!-- decode是扫描结果的函数,torch用于是否需要打开手电筒,init用于检查该设备是否能够调用摄像头的权限,camera可用于打开前面或者后面摄像头 --> <qrcode-drop-zone @decode="onDecode"> <qrcode-stream @decode="onDecode" :torch="torchActive" @init="onInit" :camera="camera"> <div class="qr-scanner"> <div class="box"> <div class="line"></div> <div class="angle"></div> </div> </div> </qrcode-stream> </qrcode-drop-zone> </div> <div class="code-button"> <van-button type="info" @click="switchCamera" plain>相机反转</van-button> <!-- <van-button type="info" @click="ClickFlash" plain>打开手电筒</van-button> --> <van-button type="info" @click="turnCameraOff" plain> {{ qrcode === true ? '关闭相机' : '打开相机' }} </van-button> </div> </div> </div> </template>
<script> // 引用vue-qrcode-reader插件 import { QrcodeStream, QrcodeDropZone } from 'vue-qrcode-reader'
export default { name: 'Approve', props: { camera: { type: String, default: 'rear' }, torchActive: { type: Boolean, default: false }, qrcode: { type: Boolean, default: false }, noStreamApiSupport: { type: Boolean, default: false } }, data () { return { error: '' } }, created () {},
components: { // 注册组件 QrcodeStream, QrcodeDropZone // QrcodeCapture }, methods: { // 扫码结果回调 onDecode (result) { this.$emit('onDecode', result) }, // 相机反转 switchCamera () { this.$emit('switchCamera') }, // 关闭、打开相机 turnCameraOff () { this.$emit('turnCameraOff') }, // 打开手电筒 ClickFlash () { this.$emit('ClickFlash') }, // 检查是否调用摄像头 onInit (promise) { this.$emit('onInit', promise) } } } </script> <style lang="scss" scoped> ::v-deep { .qrcode-stream-camera { height: 85vh; } } .code { // width: 500px; // height: 500px; width: 100%; height: 100%; margin: 88px auto 0; .qrcode-drop-zone { width: 100%; height: 85vh; margin: 0 auto; background: #000; } .code-button { width: 100%; position: fixed; left: 0; bottom: 10px; display: flex; .van-button { flex: 1; border: none; } }
.qr-scanner { width: 100%; height: 85vh; background-image: linear-gradient(0deg, transparent 24%, rgba(32, 255, 77, 0.1) 25%, rgba(32, 255, 77, 0.1) 26%, transparent 27%, transparent 74%, rgba(32, 255, 77, 0.1) 75%, rgba(32, 255, 77, 0.1) 76%, transparent 77%, transparent), linear-gradient(90deg, transparent 24%, rgba(32, 255, 77, 0.1) 25%, rgba(32, 255, 77, 0.1) 26%, transparent 27%, transparent 74%, rgba(32, 255, 77, 0.1) 75%, rgba(32, 255, 77, 0.1) 76%, transparent 77%, transparent); background-size: 3rem 3rem; background-position: -1rem -1rem; position: relative; background-color: #1110;
/* background-color: #111; */ }
.qr-scanner .box { width: 100%; height: 100%; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); overflow: hidden; /* background: url('http://resource.beige.world/imgs/gongconghao.png') no-repeat center center; */ }
.qr-scanner .line { height: calc(100% - 2px); width: 100%; background: linear-gradient(180deg, rgba(0, 255, 51, 0) 43%, #00ff33 211%); border-bottom: 3px solid #00ff33; 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: 3vw; height: 3vw;
border: 0.2rem solid transparent; }
.qr-scanner .box:after, .qr-scanner .box:before { top: 0; border-top-color: #00ff33; }
.qr-scanner .angle:after, .qr-scanner .angle:before { bottom: 0; border-bottom-color: #00ff33; }
.qr-scanner .box:before, .qr-scanner .angle:before { left: 0; border-left-color: #00ff33; }
.qr-scanner .box:after, .qr-scanner .angle:after { right: 0; border-right-color: #00ff33; }
@keyframes radar-beam { 0% { transform: translateY(-100%); }
100% { transform: translateY(0); } } } </style>
2.index.vue
<template> <div class="qrcode"> <div class="box"> <qrcode :qrcode="qrcode" :camera="camera" :torchActive="torchActive" @switchCamera="switchCamera" @ClickFlash="ClickFlash" @turnCameraOff="turnCameraOff" @onDecode="onDecode" @onInit="onInit" /> </div> </div> </template> <script> export default { data () { return { qrcode: true, torchActive: false, camera: 'front' } }, mounted () {}, methods: { // 打开相机 clickCode () { // camera:: 'rear'--前摄像头,'front'后摄像头,'off'关闭摄像头,会获取最后一帧显示,'auto'开始获取摄像头 this.qrcode = true this.camera = 'rear' }, // 扫码结果回调 onDecode (result) { console.log(result) this.turnCameraOff() }, // 相机反转 switchCamera () { switch (this.camera) { case 'front': this.camera = 'rear' break case 'rear': this.camera = 'front' break default: this.$toast('错误') } }, // 关闭相机?????? turnCameraOff () { // this.camera = 'off' this.qrcode = !this.qrcode if (this.camera === 'off') { this.camera = 'front' } else { this.camera = 'off' } }, // 打开手电筒 ClickFlash () { switch (this.torchActive) { case true: this.torchActive = false break case false: this.torchActive = true break default: this.$toast('错误') } },// 检查是否调用摄像头 async onInit (promise) { try { await promise } catch (error) { if (error.name === 'StreamApiNotSupportedError') { } else if (error.name === 'NotAllowedError') { this.errorMessage = 'Hey! I need access to your camera' } else if (error.name === 'NotFoundError') { this.errorMessage = 'Do you even have a camera on your device?' } else if (error.name === 'NotSupportedError') { this.errorMessage = 'Seems like this page is served in non-secure context (HTTPS, localhost or file://)' } else if (error.name === 'NotReadableError') { this.errorMessage = "Couldn't access your camera. Is it already in use?" } else if (error.name === 'OverconstrainedError') { this.errorMessage = "Constraints don't match any installed camera. Did you asked for the front camera although there is none?" } else { this.errorMessage = 'UNKNOWN ERROR: ' + error.message } } } }, components: { qrcode: () => import('./qrcode-reader.vue') } } </script> <style lang="scss" scoped> .qrcode { .box { width: 100%; } } </style>
3.注意点
-
调用的是浏览器的摄像头
-
该插件只适用于扫描二维码,而不适用于扫描条形码。
- vue-qrcode-reader这个插件只适用于本地调试或者带有https的域名,http是不能用这个插件的。具体原因请查看官方文档