1.vue组件
<template>
<qrcode-stream class="js-qr" :camera="camera" :torch="torchActive" @decode="onDecode" @init="onInit">
<div v-show="qrcode">
<div class="qr-scanner">
<div class="qr-scanner-top">
<span class="close" @click="$emit('oncloseJsQr', true)">×</span>
<van-uploader class="pictures" :after-read="afterRead">
<span>相册</span>
</van-uploader>
</div>
<div class="box">
<div class="line"></div>
<div class="angle"></div>
</div>
<div class="txt">
将二维码放入框内,即自动扫描
</div>
</div>
</div>
</qrcode-stream>
</template>
<script>
// 引入
import { QrcodeStream } from 'vue-qrcode-reader' // "vue-qrcode-reader": "3.1.8"
import compression from '@/utils/compression'
import QrcodeDecoder from 'qrcode-decoder' // 0.1.2
export default {
// 注册
components: { QrcodeStream },
data() {
return {
result: '', // 扫码结果信息
error: '', // 错误信息
// show: false,
// qrcode: false,
qrcode: false,
torchActive: false,
camera: 'auto'
}
},
mounted() {},
methods: {
onDecode(result) {
// alert(result)
this.result = result
this.checkCode(result)
},
checkCode(code) {
clearTimeout(this.backTimer)
this.backTimer = setTimeout(() => {
// const validated = validERC20(code) || validOMNI(code) || validTRC20(code)
// console.log(validated)
// if (validated) {
this.$emit('onsuccess', code)
this.$emit('oncloseJsQr', true)
// } else {
// console.log('不支持的二维码')
// this.$toast('不支持的二维码')
// }
}, 50)
},
afterRead(uploadRes) {
// const base64Img = file.content
compression(uploadRes.content, 500, this.dealImg)
},
dealImg(base64Img) {
const qr = new QrcodeDecoder()
qr.decodeFromImage(base64Img).then(decodeRes => {
const code = decodeRes.data
this.checkCode(code)
})
},
async onInit(promise) {
const { capabilities } = await promise
const TORCH_IS_SUPPORTED = !!capabilities.torch
try {
await promise
this.qrcode = true
} catch (error) {
if (error.name === 'NotAllowedError') {
this.error = 'ERROR: 您需要授予相机访问权限'
} else if (error.name === 'NotFoundError') {
this.error = 'ERROR: 这个设备上没有摄像头'
} else if (error.name === 'NotSupportedError') {
this.error = 'ERROR: 所需的安全上下文(HTTPS、本地主机)'
} else if (error.name === 'NotReadableError') {
this.error = 'ERROR: 相机被占用'
} else if (error.name === 'OverconstrainedError') {
this.error = 'ERROR: 安装摄像头不合适'
} else if (error.name === 'StreamApiNotSupportedError') {
this.error = 'ERROR: 此浏览器不支持流API'
}
this.$toast(this.error)
}
}
}
}
</script>
<style lang="scss" scoped>
.js-qr {
width: 100%;
height: 100vh;
position: fixed !important;
top: 0;
left: 0;
z-index: 1000 !important;
}
.error {
font-weight: bold;
color: red;
}
.cameraMessage {
width: 100%;
height: 60px;
}
.qr-scanner {
position: fixed;
top: 0;
left: 0;
z-index: 1000;
background-size: 3rem 3rem;
background-position: -1rem -1rem;
width: 100%;
height: 100vh;
background-color: #1110;
.qr-scanner-top {
margin-top: 30px;
padding: 0 30px;
display: flex;
justify-content: space-between;
align-items: center;
color: #fff;
font-size: 18px;
.close {
font-size: 36px;
}
}
.box {
width: 213px;
height: 213px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
overflow: hidden;
border: 0.1rem solid rgba(0, 255, 51, 0.2);
}
.txt {
width: 100%;
height: 35px;
line-height: 35px;
font-size: 16px;
text-align: center;
margin: 0 auto;
position: absolute;
top: 70%;
left: 0;
color: #fff;
}
.myQrcode {
text-align: center;
color: #00ae10;
}
.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;
}
.box:after,
.box:before,
.angle:after,
.angle:before {
content: '';
display: block;
position: absolute;
width: 3vw;
height: 3vw;
border: 0.2rem solid transparent;
}
.box:after,
.box:before {
top: 0;
border-top-color: #00ff33;
}
.angle:after,
.angle:before {
bottom: 0;
border-bottom-color: #00ff33;
}
.box:before,
.angle:before {
left: 0;
border-left-color: #00ff33;
}
.box:after,
.angle:after {
right: 0;
border-right-color: #00ff33;
}
}
@keyframes radar-beam {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(0);
}
}
</style>
2.compression
export default function(base64, w, callback) {
const newImage = new Image()
let quality = 0.6 // 压缩系数0-1之间
newImage.src = base64
newImage.setAttribute('crossOrigin', 'Anonymous') // url为外域时需要
let imgWidth, imgHeight
newImage.onload = function() {
imgWidth = this.width
imgHeight = this.height
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
if (Math.max(imgWidth, imgHeight) > w) {
if (imgWidth > imgHeight) {
canvas.width = w
canvas.height = (w * imgHeight) / imgWidth
} else {
canvas.height = w
canvas.width = (w * imgWidth) / imgHeight
}
} else {
canvas.width = imgWidth
canvas.height = imgHeight
quality = 0.6
console.log(11)
}
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(this, 0, 0, canvas.width, canvas.height)
const base641 = canvas.toDataURL('image/png', quality) // 压缩语句
// 如想确保图片压缩到自己想要的尺寸,如要求在50-150kb之间,请加以下语句,quality初始值根据情况自定
// while (base64.length / 1024 > 150) {
// quality -= 0.01;
// base64 = canvas.toDataURL("image/jpeg", quality);
// }
// 防止最后一次压缩低于最低尺寸,只要quality递减合理,无需考虑
// while (base64.length / 1024 < 50) {
// quality += 0.001;
// base64 = canvas.toDataURL("image/jpeg", quality);
// }
callback(base641) // 必须通过回调函数返回,否则无法及时拿到该值
}
}