H5-扫描二维码及条形码

一、声明及注意事项

  • 代码需要部署所在的服务器需要https;否则功能不能实现
  • 需要的npm包:@ericblade/quagga2,jsqr

二、具体实现

2.1 安装包

yarn add jsqr @ericblade/quagga2

2.2 封装一个组件Scanner,这里以Vue为例

2.2.1 template部分
<template>
    <div class="scaner" ref="scaner">
        <div class="banner">
            <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>
2.2.2 script部分
import jsQR from 'jsqr';
import Quagga from '@ericblade/quagga2';
  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,
                containerWidth: null,
                active: false,
                stopOnScanned: true
            }
        },
        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 () {
                let _this = this
                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;
                    // 这里是条形码扫描需要的文件
                    let dataurl = this.$refs.canvas.toDataURL()
                    try {
                        Quagga.decodeSingle({
                            src: dataurl,
                            numOfWorkers: 0,  // Needs to be 0 when used within node
                            inputStream: {
                                size: 800  // restrict input-size to be 800px in width (long-side)
                            },
                            decoder: {
                                readers: ["code_128_reader"] // List of active readers
                            },
                        }, function(result) {

                            if(result && result.codeResult && result.codeResult.code) {

                                let { code } =  result.codeResult
                                // this.drawBox(code.location);
                                console.log('code',code)
                                _this.found(code);

                            } else {
                            // 这里进行二维码扫描
                                let qrresult = jsQR(imageData.data, imageData.width, imageData.height)
                                if(qrresult && qrresult.data){
                                    _this.found(qrresult.data);
                                }
                            }
                            _this.run();

                        });


                    } 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 () {

        },
        beforeDestroy () {
            this.fullStop();
        }
    }
2.2.3 style部分
.scaner {
        background: #000000;
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
    }
    .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: center;
    }
    .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}
    }

3、把上面合在一起就OK

4、如果你觉得有用话,请点赞

通过使用VueH5的方式,可以实现在网页上扫描二维码的功能。你可以使用Vue框架结合@zxing/library库来实现这个功能。首先,需要安装Vue和@zxing/library库,你可以通过使用npm install命令来安装它们。然后,使用video标签调用后台摄像头,并使用条形码解析插件对二维码进行解析。在这个过程中,你可以根据自己的需求选择是否使用Vant框架,因为Vant框架并不是必需的。<span class=&quot;em&quot;>1</span><span class=&quot;em&quot;>2</span> #### 引用[.reference_title] - *1* [vue+H5实现扫码条形码二维码功能](https://blog.csdn.net/weixin_43216105/article/details/120311799)[target=&quot;_blank&quot; data-report-click={&quot;spm&quot;:&quot;1018.2226.3001.9630&quot;,&quot;extra&quot;:{&quot;utm_source&quot;:&quot;vip_chatgpt_common_search_pc_result&quot;,&quot;utm_medium&quot;:&quot;distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1&quot;}}] [.reference_item style=&quot;max-width: 50%&quot;] - *2* [vue整合SSM项目实战](https://download.csdn.net/download/m0_55755339/88241603)[target=&quot;_blank&quot; data-report-click={&quot;spm&quot;:&quot;1018.2226.3001.9630&quot;,&quot;extra&quot;:{&quot;utm_source&quot;:&quot;vip_chatgpt_common_search_pc_result&quot;,&quot;utm_medium&quot;:&quot;distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1&quot;}}] [.reference_item style=&quot;max-width: 50%&quot;] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值