|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- <template>
- <div class="scaner" ref="scaner">
- <div class="banner" v-if="showBanner">
- <i class="close_icon" @click="() => showBanner = false"></i>
- <p class="text">若当前浏览器无法扫码,请切换其他浏览器尝试</p>
- </div>
- <div class="cover">
- <p class="line"></p>
- <span class="square top left"></span>
- <span class="square top right"></span>
- <span class="square bottom right"></span>
- <span class="square bottom left"></span>
- <p class="tips">将二维码放入框内,即可自动扫描</p>
- </div>
- <video
- v-show="showPlay"
- class="source"
- ref="video"
- :width="videoWH.width"
- :height="videoWH.height"
- controls
- ></video>
- <canvas v-show="!showPlay" ref="canvas" />
- <button v-show="showPlay" @click="run">开始</button>
- </div>
- </template>
-
- <script>
-
- // eslint-disable-next-line no-unused-vars
- import adapter from 'webrtc-adapter';
- import jsQR from 'jsqr';
-
- export default {
- name: 'Scaner',
- props: {
- // 使用后置相机
- useBackCamera: {
- type: Boolean,
- default: true
- },
- // 扫描识别后停止
- stopOnScaned: {
- type: Boolean,
- default: true
- },
- drawOnfound: {
- type: Boolean,
- default: true
- },
- // 线条颜色
- lineColor: {
- type: String,
- default: '#03C03C'
- },
- // 线条宽度
- lineWidth: {
- type: Number,
- default: 2
- },
- // 视频宽度
- videoWidth: {
- type: Number,
- default: document.documentElement.clientWidth || document.body.clientWidth
- },
- // 视频高度
- videoHeight: {
- type: Number,
- default: document.documentElement.clientHeight - 48 || document.body.clientHeight - 48
- },
- responsive: {
- type: Boolean,
- default: false
- }
- },
- data () {
- return {
- showPlay: false,
- showBanner: true,
- containerWidth: null,
- active: false
- }
- },
- computed: {
- videoWH () {
- if (this.containerWidth) {
- const width = this.containerWidth;
- const height = width * 0.75;
- return { width, height };
- }
- return { width: this.videoWidth, height: this.videoHeight };
- }
- },
- watch: {
- active: {
- immediate: true,
- handler(active) {
- if (!active) {
- this.fullStop();
- }
- }
- }
- },
- methods: {
- // 画线
- drawLine (begin, end) {
- this.canvas.beginPath();
- this.canvas.moveTo(begin.x, begin.y);
- this.canvas.lineTo(end.x, end.y);
- this.canvas.lineWidth = this.lineWidth;
- this.canvas.strokeStyle = this.lineColor;
- this.canvas.stroke();
- },
- // 画框
- drawBox (location) {
- if (this.drawOnfound) {
- this.drawLine(location.topLeftCorner, location.topRightCorner);
- this.drawLine(location.topRightCorner, location.bottomRightCorner);
- this.drawLine(location.bottomRightCorner, location.bottomLeftCorner);
- this.drawLine(location.bottomLeftCorner, location.topLeftCorner);
- }
- },
- tick () {
- if (this.$refs.video && this.$refs.video.readyState === this.$refs.video.HAVE_ENOUGH_DATA) {
- this.$refs.canvas.height = this.videoWH.height;
- this.$refs.canvas.width = this.videoWH.width;
- this.canvas.drawImage(this.$refs.video, 0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
- const imageData = this.canvas.getImageData(0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
- let code = false;
- try {
- code = jsQR(imageData.data, imageData.width, imageData.height);
- } catch (e) {
- console.error(e);
- }
- if (code) {
- this.drawBox(code.location);
- this.found(code.data);
- }
- }
- this.run();
- },
- // 初始化
- setup () {
- if (this.responsive) {
- this.$nextTick(() => {
- this.containerWidth = this.$refs.scaner.clientWidth;
- });
- }
- if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
- this.previousCode = null;
- this.parity = 0;
- this.active = true;
- this.canvas = this.$refs.canvas.getContext("2d");
- const facingMode = this.useBackCamera ? { exact: 'environment' } : 'user';
- const handleSuccess = stream => {
- if (this.$refs.video.srcObject !== undefined) {
- this.$refs.video.srcObject = stream;
- } else if (window.videoEl.mozSrcObject !== undefined) {
- this.$refs.video.mozSrcObject = stream;
- } else if (window.URL.createObjectURL) {
- this.$refs.video.src = window.URL.createObjectURL(stream);
- } else if (window.webkitURL) {
- this.$refs.video.src = window.webkitURL.createObjectURL(stream);
- } else {
- this.$refs.video.src = stream;
- }
- this.$refs.video.playsInline = true;
- const playPromise = this.$refs.video.play();
- playPromise.catch(() => (this.showPlay = true));
- playPromise.then(this.run);
- };
- navigator.mediaDevices
- .getUserMedia({ video: { facingMode } })
- .then(handleSuccess)
- .catch(() => {
- navigator.mediaDevices
- .getUserMedia({ video: true })
- .then(handleSuccess)
- .catch(error => {
- this.$emit("error-captured", error);
- });
- });
- }
- },
- run () {
- if (this.active) {
- requestAnimationFrame(this.tick);
- }
- },
- found (code) {
- if (this.previousCode !== code) {
- this.previousCode = code;
- } else if (this.previousCode === code) {
- this.parity += 1;
- }
- if (this.parity > 2) {
- this.active = this.stopOnScanned ? false : true;
- this.parity = 0;
- this.$emit("code-scanned", code);
- }
- },
- // 完全停止
- fullStop () {
- if (this.$refs.video && this.$refs.video.srcObject) {
- this.$refs.video.srcObject.getTracks().forEach(t => t.stop());
- }
- }
- },
- mounted () {
- this.setup();
- },
- beforeDestroy () {
- this.fullStop();
- }
- }
- </script>
-
- <style lang="css" scoped>
- .scaner {
- background: #000000;
- position: fixed;
- top: 48PX;
- left: 0;
- width: 100%;
- height: 100%;
- height: -webkit-calc(100% - 48PX);
- height: -moz-calc(100% - 48PX);
- height: -ms-calc(100% - 48PX);
- height: -o-calc(100% - 48PX);
- height: calc(100% - 48PX);
- }
- .scaner .banner {
- width: 340PX;
- position: absolute;
- top: 16PX;
- left: 50%;
- margin-left: -170PX;
- background: #FA74A2;
- border-radius: 8PX;
- box-sizing: border-box;
- padding: 12PX;
- opacity: 0.9;
- box-shadow: 1PX 1PX 10PX rgba(0, 0, 0, 0.2);
- }
- .scaner .banner .text {
- padding: 0;
- margin: 0;
- color: #FFFFFF;
- font-size: 12PX;
- text-align: justify;
- text-align-last: left;
- }
- .scaner .banner .close_icon {
- display: inline-block;
- height: 24PX;
- width: 24PX;
- /*background: url('../assets/close.png') no-repeat center;*/
- background-size: auto 100%;
- position: absolute;
- right: 8PX;
- top: 8PX;
- }
- .scaner .cover {
- height: 220PX;
- width: 220PX;
- position: absolute;
- top:50%;
- left:50%;
- -webkit-transform: translate(-50%,-50%);
- -moz-transform: translate(-50%,-50%);
- -ms-transform: translate(-50%,-50%);
- -o-transform: translate(-50%,-50%);
- transform: translate(-50%,-50%);
- border: .5PX solid #999999;
- z-index: 1111;
- }
- .scaner .cover .line {
- width: 200PX;
- height: 1PX;
- margin-left: 10PX;
- background: #5F68E8;
- background: linear-gradient(to right, transparent, #5F68E8, #0165FF, #5F68E8, transparent);
- position: absolute;
- -webkit-animation: scan 1.75s infinite linear;
- -moz-animation: scan 1.75s infinite linear;
- -ms-animation: scan 1.75s infinite linear;
- -o-animation: scan 1.75s infinite linear;
- animation: scan 1.75s infinite linear;
- -webkit-animation-fill-mode: both;
- -moz-animation-fill-mode: both;
- -ms-animation-fill-mode: both;
- -o-animation-fill-mode: both;
- animation-fill-mode: both;
- border-radius: 1PX;
- }
- .scaner .cover .square {
- display: inline-block;
- height: 20PX;
- width: 20PX;
- position: absolute;
- }
- .scaner .cover .square.top {
- top: 0;
- border-top: 1PX solid #5F68E8;
- }
- .scaner .cover .square.left {
- left: 0;
- border-left: 1PX solid #5F68E8;
- }
- .scaner .cover .square.bottom {
- bottom: 0;
- border-bottom: 1PX solid #5F68E8;
- }
- .scaner .cover .square.right {
- right: 0;
- border-right: 1PX solid #5F68E8;
- }
- .scaner .cover .tips {
- position: absolute;
- bottom: -48PX;
- width: 100%;
- font-size: 14PX;
- color: #FFFFFF;
- opacity: 0.8;
- }
- @-webkit-keyframes scan {
- 0% {top: 0}
- 25% {top: 50PX}
- 50% {top: 100PX}
- 75% {top: 150PX}
- 100% {top: 200PX}
- }
- @-moz-keyframes scan {
- 0% {top: 0}
- 25% {top: 50PX}
- 50% {top: 100PX}
- 75% {top: 150PX}
- 100% {top: 200PX}
- }
- @-o-keyframes scan {
- 0% {top: 0}
- 25% {top: 50PX}
- 50% {top: 100PX}
- 75% {top: 150PX}
- 100% {top: 200PX}
- }
- @keyframes scan {
- 0% {top: 0}
- 25% {top: 50PX}
- 50% {top: 100PX}
- 75% {top: 150PX}
- 100% {top: 200PX}
- }
- </style>
|