依赖包jsQR
思路:通过浏览器获取视频流播放在video当中,然后设置定时器截取视频流绘制在canvas中,使用canvas获取到图片信息,再使用jsQR解析二维码。
由于开源二维码解析库识别率不高 特别模糊的二维码识别率较低 可满足部分开发需求,如需要更加精确推荐dynamsoft
dynamsoft demo地址:https://demo.dynamsoft.com/Samples/DBR/JS/2.ui-tweaking/3.read-video-with-external-control.html?ver=latest
<template>
<div class="test">
<div class="fixBox">
<div
class="iconBox"
@click="
() => {
this.$router.history.go(-1);
}
"
>
<i class="iconfont icon-fanhuitubiao"></i>
</div>
<div class="videoBox">
<div class="outVideo" v-if="isAnimation">
<video
autoPlay
ref="outVideo"
webkit-playsinline
x5-video-player-type="h5"
x5-video-player-fullscreen="true"
x-webkit-airplay="true"
playsinline
v-if="!trueFlag"
id="myVideo"
></video>
</div>
<div class="codebg" v-if="!trueFlag">
<div class="line"></div>
</div>
<canvas
id="qr-canvas"
style="width:6rem;height:6rem;"
ref="canvas"
></canvas>
</div>
</div>
</div>
</template>
<script>
import jsQR from "jsqr";
export default {
data() {
return {
photoBase: "",
trueFlag: false,
isAnimation: true
};
},
mounted() {
this.outvideo = this.$refs.outVideo;
this.cvsele = this.$refs.canvas;
this.canvas = this.cvsele.getContext("2d");
this.video = document.createElement("video");
this.$nextTick(() => {
this.getVideo();
});
},
methods: {
getVideo() {
let self = this;
try {
navigator.getUserMedia =
navigator.mediaDevices.getUserMedia ||
navigator.mediaDevices.webkitGetUserMedia ||
navigator.mediaDevices.mozGetUserMedia;
self.URL =
window.URL || window.webkitURL || window.mozURL || window.msURL;
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
//console.log(navigator.mediaDevices.getUserMedia);
navigator.mediaDevices
.getUserMedia({
video: { facingMode: { exact: "environment" } }
// video: { facingMode: "environment" }
})
.then(stream => {
this.outvideo.srcObject = stream;
this.video.srcObject = stream;
this.video.setAttribute("playsinline", true);
this.video.setAttribute("webkit-playsinline", true);
this.video.addEventListener("loadedmetadata", () => {
this.$refs.outVideo.play();
this.video.play();
this.sweep();
});
setTimeout(() => {
this.$refs.outVideo.play();
this.video.play();
}, 150);
})
.catch(function(err) {
console.log(err);
alert("请允许网站使用您的摄像头权限");
self.$router.history.go(-1);
});
} else if (navigator.getUserMedia) {
navigator
.getUserMedia({
video: true
})
.then(stream => {
this.outvideo.srcObject = stream;
this.video.srcObject = stream;
this.video.setAttribute("playsinline", true);
this.video.setAttribute("webkit-playsinline", true);
this.video.addEventListener("loadedmetadata", () => {
this.$refs.outVideo.play();
this.video.play();
this.sweep();
});
setTimeout(() => {
this.$refs.outVideo.play();
this.video.play();
}, 150);
})
.catch(function(err) {
alert(err);
});
}
} catch (err) {
console.error(err);
}
},
sweep() {
if (this.video.readyState === this.video.HAVE_ENOUGH_DATA) {
const { videoWidth, videoHeight } = this.video;
this.cvsele.width = videoWidth;
this.cvsele.height = videoHeight;
this.canvas.drawImage(this.video, 0, 0, videoWidth, videoHeight);
try {
const img = this.canvas.getImageData(0, 0, videoWidth, videoHeight);
this.imgurl = img;
const obj = jsQR(img.data, img.width, img.height, {
inversionAttempts: "dontInvert"
});
console.log(obj);
if (obj) {
const loc = obj.location;
this.draw(loc.topLeftCorner, loc.topRightCorner);
this.draw(loc.topRightCorner, loc.bottomRightCorner);
this.draw(loc.bottomRightCorner, loc.bottomLeftCorner);
this.draw(loc.bottomLeftCorner, loc.topLeftCorner);
if (obj.data) {
console.info("识别结果:", obj.data);
this.isAnimation = false;
}
} else {
// console.error("识别失败,请检查二维码是否正确!");
}
} catch (err) {
// console.error("识别失败,请检查二维码是否正确!", err);
}
}
if (this.isAnimation) {
this.timer = requestAnimationFrame(() => {
this.sweep();
});
}
},
draw(begin, end) {
this.canvas.beginPath();
this.canvas.moveTo(begin.x, begin.y);
this.canvas.lineTo(end.x, end.y);
this.canvas.lineWidth = 3;
this.canvas.strokeStyle = "red";
this.canvas.stroke();
},
},
};
</script>
<style lang="scss" scoped>
.test {
width: 100vw;
height: 100vh;
overflow: hidden;
}
.biginSearch {
outline: none;
border: none;
width: 3.15rem;
height: 0.8rem;
background: var(--themeColorzc);
border-radius: 0.1rem;
color: #fff;
font-weight: 700;
margin-top: 0.3rem;
font-size: 0.3rem;
}
.fixBox {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
// background: rgba(47, 47, 47, 0.81);
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.videoBox {
// border: 2px solid rgba(247, 241, 241, 0.81);
// width: 6rem;
// height: 6rem;
// overflow: hidden;
width: 100%;
height: 100%;
position: relative;
background-color: black;
.outVideo {
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 999;
background-color: black;
video {
width: 100%;
height: 100%;
object-fit: fill;
}
}
}
canvas {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.codebg {
position: absolute;
width: 6rem;
height: 6rem;
margin: 0px auto;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 999;
/*此处为了居中*/
// background: url(img/ewm1.jpg) center top no-repeat;
/*二维码*/
}
.line {
position: absolute;
left: 0px;
z-index: 2;
height: 3px;
width: 6rem;
/* background: url(img/share/dapai.png) no-repeat; */
/*上下扫的线*/
background-color: var(--themeColorzc);
opacity: 0.5;
/*动画效果*/
animation: myScan 1s infinite alternate;
-webkit-animation: myScan 1s infinite alternate;
}
@keyframes myScan {
from {
top: 0px;
}
to {
top: 300px;
}
}
@font-face {
font-family: "iconfont"; /* Project id 2570754 */
src: url("//at.alicdn.com/t/font_2570754_w4gqnoegfag.woff2?t=1629957776725")
format("woff2"),
url("//at.alicdn.com/t/font_2570754_w4gqnoegfag.woff?t=1629957776725")
format("woff"),
url("//at.alicdn.com/t/font_2570754_w4gqnoegfag.ttf?t=1629957776725")
format("truetype");
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-fanhuitubiao:before {
font-size: 0.48rem;
color: #fff;
content: "\e65c";
}
.iconBox {
position: absolute;
top: 0.3rem;
left: 0.3rem;
}
</style>