Browse Source

二维码扫描动画效果优化v0.1

lwx_v6
liwenxuan 10 months ago
parent
commit
13a80732bf
  1. 57
      package-lock.json
  2. 1
      package.json
  3. 426
      src/views/home/scanQrCode.vue

57
package-lock.json

@ -26,6 +26,7 @@
"ts-md5": "^1.3.1",
"vue": "^3.5.10",
"vue-pdf-embed": "^1.2.1",
"vue-qrcode-reader": "^5.7.0",
"vue-router": "^4.4.5",
"vue3-pdf-embed": "^1.1.7",
"vue3-pdfjs": "^0.1.6"
@ -1458,6 +1459,16 @@
"node": ">=10.13.0"
}
},
"node_modules/@types/dom-webcodecs": {
"version": "0.1.13",
"resolved": "https://registry.npmmirror.com/@types/dom-webcodecs/-/dom-webcodecs-0.1.13.tgz",
"integrity": "sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ=="
},
"node_modules/@types/emscripten": {
"version": "1.40.0",
"resolved": "https://registry.npmmirror.com/@types/emscripten/-/emscripten-1.40.0.tgz",
"integrity": "sha512-MD2JJ25S4tnjnhjWyalMS6K6p0h+zQV6+Ylm+aGbiS8tSn/aHLSGNzBgduj6FB4zH0ax2GRMGYi/8G1uOxhXWA=="
},
"node_modules/@types/estree": {
"version": "1.0.6",
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.6.tgz",
@ -1955,6 +1966,15 @@
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/barcode-detector": {
"version": "2.2.2",
"resolved": "https://registry.npmmirror.com/barcode-detector/-/barcode-detector-2.2.2.tgz",
"integrity": "sha512-JcSekql+EV93evfzF9zBr+Y6aRfkR+QFvgyzbwQ0dbymZXoAI9+WgT7H1E429f+3RKNncHz2CW98VQtaaKpmfQ==",
"dependencies": {
"@types/dom-webcodecs": "^0.1.11",
"zxing-wasm": "1.1.3"
}
},
"node_modules/base": {
"version": "0.11.2",
"resolved": "https://registry.npmmirror.com/base/-/base-0.11.2.tgz",
@ -5981,6 +6001,11 @@
"resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz",
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="
},
"node_modules/sdp": {
"version": "3.2.0",
"resolved": "https://registry.npmmirror.com/sdp/-/sdp-3.2.0.tgz",
"integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw=="
},
"node_modules/semver": {
"version": "7.6.3",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz",
@ -7693,6 +7718,18 @@
"vue": "^2.x || ^3.x"
}
},
"node_modules/vue-qrcode-reader": {
"version": "5.7.0",
"resolved": "https://registry.npmmirror.com/vue-qrcode-reader/-/vue-qrcode-reader-5.7.0.tgz",
"integrity": "sha512-NuLBK3madyMhM35n2libd0i5GH3Rcvq2d7qB1Qyf5FqdIdC6GaMEC8Wi8v0HFuv5V5aptOLhHVBfSczBpO3lvA==",
"dependencies": {
"barcode-detector": "2.2.2",
"webrtc-adapter": "8.2.3"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/vue-router": {
"version": "4.4.5",
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.4.5.tgz",
@ -7774,6 +7811,18 @@
"resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="
},
"node_modules/webrtc-adapter": {
"version": "8.2.3",
"resolved": "https://registry.npmmirror.com/webrtc-adapter/-/webrtc-adapter-8.2.3.tgz",
"integrity": "sha512-gnmRz++suzmvxtp3ehQts6s2JtAGPuDPjA1F3a9ckNpG1kYdYuHWYpazoAnL9FS5/B21tKlhkorbdCXat0+4xQ==",
"dependencies": {
"sdp": "^3.2.0"
},
"engines": {
"node": ">=6.0.0",
"npm": ">=3.10.0"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
@ -7969,6 +8018,14 @@
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/zxing-wasm": {
"version": "1.1.3",
"resolved": "https://registry.npmmirror.com/zxing-wasm/-/zxing-wasm-1.1.3.tgz",
"integrity": "sha512-MYm9k/5YVs4ZOTIFwlRjfFKD0crhefgbnt1+6TEpmKUDFp3E2uwqGSKwQOd2hOIsta/7Usq4hnpNRYTLoljnfA==",
"dependencies": {
"@types/emscripten": "^1.39.10"
}
}
}
}

1
package.json

@ -28,6 +28,7 @@
"ts-md5": "^1.3.1",
"vue": "^3.5.10",
"vue-pdf-embed": "^1.2.1",
"vue-qrcode-reader": "^5.7.0",
"vue-router": "^4.4.5",
"vue3-pdf-embed": "^1.1.7",
"vue3-pdfjs": "^0.1.6"

426
src/views/home/scanQrCode.vue

@ -1,287 +1,181 @@
<template>
<div class="scanCode">
<div class="container">
<div class="qrcode">
<!-- 新增扫描线 -->
<div class="scan-line"></div>
<!-- 新增边框装饰 -->
<div class="border-decor top-left"></div>
<div class="border-decor top-right"></div>
<div class="border-decor bottom-left"></div>
<div class="border-decor bottom-right"></div>
<div class="btn">
<div class="left-back" style="float: left;">
<el-icon @click="clickBack" style="border: white solid 0px;">
<Back />
</el-icon>
</div>
<!-- <div class="right-file">
<van-uploader
v-model="fileList"
:preview-image="false"
:after-read="dealSelectFiles"
>
<el-icon><Picture /></el-icon>
</van-uploader>
</div> -->
<div style="height: 100vh">
<qrcode-stream @detect="onDecode" @error="onInit" style="height: 100%">
<div>
<div class="qr-scanner">
<!--顶部左边的返回箭头-->
<div>
<!-- <van-icon class="scanImg" @click="onClickLeft" name="arrow-left" /> -->
<button @click="onClickLeft" name="arrow-left" ></button>
</div>
<!--中间的扫码框-->
<div class="box">
<div class="line"></div>
<div class="angle"></div>
</div>
<div class="txt">
将二维码/条码放入框内即自动扫描
</div>
<div id="reader"></div>
</div>
</div>
<!--<div class="btn">
<div class="left-back">
<el-icon @click="clickBack">
<Back />
</el-icon>
</div>
<div class="right-file">
<van-uploader
v-model="fileList"
:preview-image="false"
:after-read="dealSelectFiles"
>
<el-icon><Picture /></el-icon>
</van-uploader>
</div>
</div> -->
</div>
</qrcode-stream>
</div>
</template>
<script setup lang="ts">
import {QrcodeStream} from "vue-qrcode-reader";
import {ref} from "vue";
<script setup>
import { reactive } from "vue";
import { defineComponent, toRefs, onMounted, onUnmounted } from "vue";
import { Html5Qrcode } from "html5-qrcode";
import { useRouter } from 'vue-router'
const router = useRouter()
const state = reactive({
html5QrCode: null,
fileList: [],
});
const start = () => {
state.html5QrCode
.start(
{ facingMode: "environment" },
{
fps: 5,
qrbox: { width: 300, height: 210 },
aspectRatio: 1.7,//
},
(decodedText, decodedResult) => {
window.location.href = decodedText;
if (state.html5QrCode&&state.html5QrCode.isScanning) {
stop();
}
console.log("decodedText", decodedText);
console.log("decodedResult", decodedResult);
}
)
.catch((err) => {
console.log("扫码错误信息", err);
let message = ""; //
if (typeof err == "string") {
message = "二维码识别失败!";
alert(message)
} else {
if (err.name == "NotAllowedError") {
message = "您需要授予相机访问权限!";
}
if (err.name == "NotFoundError") {
message = "这个设备上没有摄像头!";
}
if (err.name == "NotSupportedError") {
message =
"摄像头访问只支持在安全的上下文中,如https或localhost!";
}
if (err.name == "NotReadableError") {
message = "相机被占用!";
}
if (err.name == "OverconstrainedError") {
message = "安装摄像头不合适!";
}
if (err.name == "StreamApiNotSupportedError") {
message = "此浏览器不支持流API!";
}
alert(message)
}
});
};
const clickBack = () => {
//console.log(1)
if (state.html5QrCode&&state.html5QrCode.isScanning) {
stop();
}
router.back();
}
const getCameras = () => {
Html5Qrcode.getCameras()
.then((devices) => {
if (devices && devices.length) {
state.html5QrCode = new Html5Qrcode("reader");
start();
}
})
.catch((err) => {
console.log(err)
alert("摄像头无访问权限!");
});
};
const stop = () => {
state.html5QrCode
.stop()
.then((ignore) => {
console.log("停止扫码", ignore);
})
.catch((err) => {
console.log(err);
alert("停止扫码失败");
});
};
const dealSelectFiles = () => {
try {
window.qrcode.callback = (result) => {
alert("成功了,结果是:" + result);
}; // get select files.
let file = state.fileList[0].file;
var reader = new FileReader();
reader.onload = (function () {
return function (e) {
window.qrcode.decode(e.target.result);
};
})(file);
reader.readAsDataURL(file);
} catch (error) {
alert("图片识别失败!");
}
};
onMounted(() => {
getCameras();
});
onUnmounted(() => {
//
if (state.html5QrCode&&state.html5QrCode.isScanning) {
stop();
}
});
</script>
//
const dataList = ref('')
const result = ref(true)
const error = ref('')
<style lang="scss" scoped>
//
@keyframes scanLine {
0% { transform: translateY(-100%); }
100% { transform: translateY(300%); }
}
@keyframes borderPulse {
0% { opacity: 0.8; }
50% { opacity: 0.2; }
100% { opacity: 0.8; }
}
.scan-line {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 90%;
height: 2px;
background: linear-gradient(180deg, rgba(0,255,255,0) 0%, rgba(51,204,255,1) 50%, rgba(0,255,255,0) 100%);
animation: scanLine 2s infinite linear;
z-index: 2;
//
const onDecode = (res: any) => {
dataList.value = res
result.value = false
console.log('你好',dataList.value)
alert(dataList.value)
//showSuccessToast('')---------------------------------------
//
//
}
.border-decor {
position: absolute;
width: 20px;
height: 20px;
border: 2px solid #33ccff;
animation: borderPulse 1.5s infinite ease-in-out;
&.top-left {
top: -1px;
left: -1px;
border-right: none;
border-bottom: none;
}
&.top-right {
top: -1px;
right: -1px;
border-left: none;
border-bottom: none;
}
&.bottom-left {
bottom: -1px;
left: -1px;
border-right: none;
border-top: none;
}
&.bottom-right {
bottom: -1px;
right: -1px;
border-left: none;
border-top: none;
//
const onInit = async (promise: any) => {
console.log('初始化摄像头',promise)
try {
await promise
}catch (err: any) {
if (err.name === 'NotAllowedError') {
error.value = 'ERROR: 您需要授予相机访问权限';
} else if (err.name === 'NotFoundError') {
error.value = 'ERROR: 这个设备上没有摄像头';
} else if (err.name === 'NotSupportedError') {
error.value = 'ERROR: 所需的安全上下文(HTTPS、本地主机)';
} else if (err.name === 'NotReadableError') {
error.value = 'ERROR: 相机被占用';
} else if (err.name === 'OverconstrainedError') {
error.value = 'ERROR: 安装摄像头不合适';
} else if (err.name === 'StreamApiNotSupportedError') {
error.value = 'ERROR: 此浏览器不支持流API';
}
}
}
//
const onClickLeft = () => {
history.back();
}
</script>
<style scoped>
:deep(i.van-badge__wrapper.van-icon.van-icon-arrow-left.scanImg){
font-size: 40px;
color: white;
}
.scanImg{
margin-top: 30px;
margin-left: 10px;
}
.error {
font-weight: bold;
color: red;
}
.cameraMessage {
width: 100%;
height: 60px;
}
.qr-scanner {
background-size: 3rem 3rem;
background-position: -1rem -1rem;
width: 100%;
/* height: 100%; */
height: 100vh;
/* height: 288px; */
position: relative;
background-color: #1110;
}
.qr-scanner .box {
width: 213px;
height: 213px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
overflow: hidden;
border: 1px solid #3aa5ff;
}
.qr-scanner .txt {
width: 100%;
height: 35px;
line-height: 35px;
font-size: 14px;
text-align: center;
/* color: #f9f9f9; */
margin: 0 auto;
position: absolute;
top: 70%;
left: 0;
}
.qr-scanner .myQrcode {
text-align: center;
color: #3aa5ff;
}
.qr-scanner .line {
height: calc(100% - 2px);
width: 100%;
background: linear-gradient(180deg, rgba(0, 255, 51, 0) 43%, #3aa5ff 211%);
border-bottom: 1px solid #3aa5ff;
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;
}
.qr-scanner .box:after,
.qr-scanner .box:before {
top: 0;
border-top-color: #3aa5ff;
}
.scanCode {
height: 100vh;
display: flex;
flex-direction: column;
background: rgba(0, 0, 0);
}
//
.container {
position: relative; //
.qr-scanner .angle:after,
.qr-scanner .angle:before {
bottom: 0;
border-bottom-color: #3aa5ff;
}
#reader {
position: relative; //
&::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: calc(100% - 4px);
height: calc(100% - 4px);
border: 2px solid rgba(255,255,255,0.1);
}
.qr-scanner .box:before,
.qr-scanner .angle:before {
left: 0;
border-left-color: #3aa5ff;
}
.qrcode {
position: relative; //
.qr-scanner .box:after,
.qr-scanner .angle:after {
right: 0;
border-right-color: #3aa5ff;
}
.btn {
flex: 1;
padding-top: 2vw;
display: flex;
padding-left: 3vw;
color: #fff;
font-size: 6vw;
align-items: flex-start;
/* border: green solid 7px; */
@keyframes radar-beam {
0% {
transform: translateY(-100%);
}
</style>
100% {
transform: translateY(0);
}
}
</style>
Loading…
Cancel
Save