uniapp_扫一扫组件(摄像头实时扫描二维码,可切换前置、后置摄像头)

功能

  1. 支持通过摄像头实时扫描二维码,可切换前置、后置摄像头,扫描成功或失败有相应提示及事件触发。
  2. 能选择本地图片进行二维码扫描,同样有对应成功、失败反馈。
  3. 界面有关闭、切换摄像头、选择相册等交互按钮。

知识点

  1. 组件使用:在HTML中引入组件,绑定相关自定义事件,布局界面元素并设置样式,含动画效果展示。
  2. 组件实现
    • 引入Html5Qrcode等库,在data中定义相关数据。
    • 利用生命周期钩子做初始化、销毁等操作。
    • 实现多个方法,如获取摄像头、关闭组件、切换摄像头、启动/停止扫描、使用本地图片扫描及对应扫描结果处理等方法。
  • 使用

  •         <BarScan
                v-if="qrcodeShow"
                ref="qrcode"
                @ok="getResult"     
                @err="geterror"
                @close="qrcodeShow = false"
            ></BarScan>
    
  • 组件源码

  • <template>
        <div class="qrcode">
            <div class="close" @click="closeComponents">
                <img src="@/static/取消.svg" alt="" style="height:20px">
            </div>
            <rjLoad v-if="!loaded" style='z-index: 99999;'></rjLoad>
            <div id="reader" style="height: 100vh;width:100%;"></div>
            <!-- 使用本地图片或者切换摄像头 -->
            <div class="switch">
                <div class="switch-item" @click="switchCamera">
                    <div class="img-box">
                        <img src="@/static/切换.svg" alt="">
    
                    </div>
                    <p>切换</p>
                </div>
                <div class="switch-item" @click="useLocalImage">
                    <div class="img-box">
                        <img src="@/static/相册.svg" alt="">
    
                    </div>
                    <p>相册</p>
                </div>
            </div>
    
        </div>
    </template>
    
    <script>
    import { Html5Qrcode } from "html5-qrcode";
    import rjLoad from "@/components/rj-load/RjLoad"
    import { conforms } from "lodash";
    export default {
        components: {
            rjLoad
        },
        data() {
            return {
                html5QrCode: null,
                loaded: false,
                type:true,
            }
        },
        created() {
            this.getCameras()
    
        },
        beforeDestroy() {
            try{
                this.stop()
            }catch(e){
                //TODO handle the exception
            }
        },
        mounted() {
        },
    	onShow(){
    		this.getCameras()
    	},
        methods: {
            getCameras() {
    			this.loaded = false
                Html5Qrcode.getCameras()
                    .then((devices) => {
                        if (devices && devices.length) {
                            this.html5QrCode = new Html5Qrcode("reader");
                            this.start();
                        }
                    })
                    .catch((err) => {
                        // handle err
                        this.html5QrCode = new Html5Qrcode("reader");
                        this.error = " 请授予相机访问权限 "
                        uni.showToast({
                            title: this.error,
                            icon: "error",
                            duration: 2000,
                        })
                        this.$emit("err", this.error)
                    });
            },
            closeComponents() {
                this.$emit("close")
            },
            switchCamera(){
                this.type=!this.type
                try{
                    this.stop()
                }catch(e){
                    //TODO handle the exception
                }
                this.getCameras()
            },
            async start() {
                let type = 'environment'
                if(!this.type){
                    type='user'
                }
                console.log(type)
                //environment后置 user前置
    			console.log('screenWidth',screen.width)
                console.log('screenHeight',screen.height)
    
                await this.html5QrCode
                    .start(
                        // environment后置摄像头 user前置摄像头 也可以传递获取摄像头时的id
                        { facingMode: type },
                        {
                            fps: 10,
                            qrbox: { width: screen.width, height: screen.height },
                            aspectRatio: screen.height/screen.width
                            // canvasWidth: screen.width,
                            // canvasHeight: screen.height,
                        },
                        (decodedText, decodedResult) => {
                            this.$emit("ok", decodedText)
                            uni.showToast({
                                title: "扫描成功",
                                icon: "success",
                                duration: 2000,
                            });
    
                        }
                    ).then(()=>{
    					console.log('启动成功')
    					setTimeout(()=>{
    						this.loaded = true
    					},500)
    				})
                    .catch((err) => {
    					console.log(err,'err')
                        this.$emit("err", err)
                    });
                
            },
            stop() {
                this.html5QrCode.stop().then((ignore) => {
                    // QR Code scanning is stopped.
                    console.log("QR Code scanning stopped.");
                })
                .catch((err) => {
                    // Stop failed, handle it.
                    console.log("Unable to stop scanning.");
                });
            },
            //使用本地文件
            useLocalImage() {
                const _that = this
                //使用本地文件需要先关闭摄像头
                try{
                    this.stop()
                }catch(e){
                    //TODO handle the exception
                }
                console.log('选择文件')
                uni.chooseFile({
                    count: 1, //默认100
                    extension: ['.jpg', '.png', '.jpeg'],
                    success: _that.successFn,
                    fail: (err) =>{
                        console.log(err)
                    }
                });
    
    
            },
            successFn(res) {
                // if (res.tempFiles[0].size / 1024 / 1024 > 20) {
                //     this.$refs.uToast.show({
                //         title: '附件大小不能超过20M',
                //         type: 'warning',
                //     })
                //     return;
                // }
                const _this = this
                this.html5QrCode
                    .scanFile(res.tempFiles[0], true)
                    .then((decodedText) => {
                        console.log('扫描成q功wqwqw,',res)
    
                        _this.$emit("ok", decodedText)
                        res.innerText = "扫码成功结果:\n" + decodedText;
                        uni.showToast({
                            title: "扫描成功",
                            icon: "success",
    
                        })
                    })
                    .catch((err) => {
                        console.log('扫描失败,',err)
                        res.innerText = "扫码失败:\n" + err;
                        uni.showToast({
                            title: "未能识别到二维码",
                            icon: "error",
                        })
    
    					this.$emit("err", err)
                    });
    
            }
        }
    }
    </script>
    
    <style lang="scss" scoped>
    .qrcode {
        position: fixed;
        z-index: 2000;
        top: 0;
        left: 0;
        height: 100%;
        width: 100%;
        background: rgba($color: #000000, $alpha: 0.48);
    }
    
    .close {
        position: absolute;
        top: 10px;
        right: 10px;
        background-color: #fff;
        font-size: 16px;
        z-index: 2001;
        height: 30px;
        width: 30px;
        display: flex;
        justify-content: center;
        align-items: center;
        text-align: center;
        border-radius: 50%;
        background: rgba($color: #000000, $alpha: 0.48);
        cursor: pointer;
    }
    
    #reader {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: 1000;
        background-color: rgba(0, 0, 0, 0.3); /* Optional: to darken the background */
        display: flex;
        align-items: center;
        justify-content: center;
    }
    #reader::before {
        content: "";
        position: absolute;
    	z-index: 1001;
        top: -4px; /* 初始位置在视图外 */
        left: 50%;
        width: 70%;
    	transform: translate(-50%,0);
        height: 8px; /* 蓝条的高度 */
    	border-radius: 60%;
    	box-shadow: 0 -10px 30px rgba(0, 123, 255, 0.8);
        background-color: rgba(0, 123, 255, 0.8); /* 蓝色条的颜色和透明度 */
        animation: scanAnimation 2s linear infinite;
    }
    
    @keyframes scanAnimation {
        0% {
            top: 25%;  /* 蓝条从视图上方开始 */
    		opacity: 0;
        }
    	50% {
    		opacity: 1;
    	}
        100% {
            top: 75%;  /* 蓝条移到视图下方 */
    		opacity: 0;
        }
    }
    .switch {
        position: absolute;
        z-index: 10001;
        bottom: 10px;
        left:0;
        box-sizing: border-box;
        color: #fff;
        width: 100%;
        display: flex;
        justify-content: space-between;
        align-items: center;
        font-size: 14px;
        padding: 10px 45px;
        .switch-item {
            display:flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            color:#fff;
            border-radius: 5px;
            .img-box{
                height: 40px;
                width: 40px;
                display: flex;
                justify-content: center;
                align-items: center;
                border-radius: 50%;
                background: rgba($color: #000000, $alpha: 0.48);
                margin-bottom: 4px;
                img{
                    height: 20px;
    
                }
            }
        }
    }
    
    button {
        display: block;
        width: 100%;
        margin: 6px;
        outline: none;
        height: 40px;
        line-height: 40px;
        color: #fff;
        background-color: #26a2ff;
        text-align: center;
        border-radius: 4px;
        border: none;
        cursor: pointer;
    }
    
    #upload-input {
        opacity: 0;
        filter: alpha(opacity=0);
        display: inline-block;
        width: 100%;
        height: 100%;
    }
    
    #upload-text {
        position: relative;
        bottom: 40px;
        user-select: none;
    }
    </style>
    

不懂可以找我

https://www.goofish.com/item?spm=a21ybx.personal.feeds.3.5e926ac2cyz0bJ&id=872662421016&categoryId=50023914

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值