web手机端扫描二维码和和条形码弹框 使用的是nutui和@zxing/library 实现

<template>
    <div class="Qqrcode">
        <m-button class="scanBtn" size="small" type="primary" @click="show=true">
            <ScanOutlined />
        </m-button>
        <m-popup position="top" overlay-class="overlayClass" round :style="{ height: '60%' }" v-model:visible="show"
            @opened="openEvent" @close="stopScan">
            <div class="scan-qrcode" ref="wrapperRef">
                <video id="video" ref="videoRef" />
                <div class="mask" ref="maskRef"></div>
            </div>
        </m-popup>
    </div>
</template>

<script setup>
import { ref, onMounted, nextTick } from "vue";
import { BrowserMultiFormatReader } from '@zxing/library';
import { ScanOutlined } from '@nancal-icon/icons-vue';
const videoRef = ref(null);
const maskRef = ref(null);
const show = ref(false);
let qrCodeReader = null;
let selectedDeviceId = '';
const emits = defineEmits(['update:value', 'ok']);
const props = defineProps({
    value: {
        type: String,
        default: '',
    },
});


function scanner() {
    qrCodeReader.decodeFromVideoDevice(selectedDeviceId, videoRef.value, (result, err) => {
        //if (result && result.resultPoints) {
        //  drawBoundingBox(result.resultPoints); // 绘制框
        //}
        if (result && result.text) {
            emits('update:value', result.text);
            emits('ok', result.text);
            show.value = false;
        }
        if (err) { }
    })
}


const drawBoundingBox = (points) => {
    const canvas = canvasRef.value;
    const canvasDom = canvas.getBoundingClientRect();
    const ctx = canvas.getContext("2d");
    // 清空画布
    ctx.clearRect(0, 0, canvasDom.width, canvasDom.height);

    // 设置绘制样式
    ctx.fillStyle = "red";
    ctx.strokeStyle = "red";
    ctx.lineWidth = 2;

    // 遍历点并绘制
    points.forEach((point) => {
        ctx.beginPath();
        ctx.arc(point.x, point.y, 5, 0, Math.PI * 2);
        ctx.fill();
        ctx.closePath();
    });
};

// 停止视频流
const stopScan = () => {
    if (qrCodeReader) {
        qrCodeReader.reset(); // 停止二维码扫描
    }
}


const openEvent = () => {
    nextTick(() => {
        scanner();
    });
}

const getRearCameraDeviceId = async () => {
        !navigator.mediaDevices ||!navigator.mediaDevices.enumerateDevices
    if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
        console.error('当前浏览器不支持获取媒体设备列表');
        return null;
    }
    try {
        const devices = await navigator.mediaDevices.enumerateDevices();
        const videoDevices = devices.filter(device => device.kind === "videoinput");
        // 尝试找到一个包含 "back" 或 "rear" 的摄像头设备
        const rearCamera = videoDevices.find(device =>
            device.label.toLowerCase().includes("back") ||
            device.label.toLowerCase().includes("rear")
        );
        if (rearCamera) {
            return rearCamera.deviceId; // 返回后置摄像头的 deviceId
        } else {
            console.warn("未找到后置摄像头,默认使用第一个摄像头");
            return videoDevices[0]?.deviceId; // 如果没有找到后置摄像头,返回第一个设备
        }
    } catch (error) {
        if (error.name === 'NotAllowedError') {
            console.error('用户拒绝了摄像头权限');
        } else if (error.name === 'NotFoundError') {
            console.error('未找到摄像头设备');
        } else {
            console.error('无法获取摄像头设备:', error);
        }
        return null;
    }
};



onMounted(async () => {
    qrCodeReader = new BrowserMultiFormatReader();  // 初始化二维码读取器
    selectedDeviceId = await getRearCameraDeviceId();
})




</script>

<style lang="less" scoped>
@keyframes translateYAnimation {
    0% {
        top: 10%;
        filter: drop-shadow(10px 10px 10px rgba(105, 120, 254, 1));
    }

    50% {
        top: 90%;
        filter: drop-shadow(10px 10px 20px rgba(105, 120, 254, 1));
    }

    100% {
        top: 10%;
        filter: drop-shadow(10px 10px 10px rgba(105, 120, 254, 1));
    }
}

.scan-qrcode {
    width: 100%;
    height: 100%;
    position: relative;
    overflow: hidden;

    video {
        width: 100%;
        height: 100%;
        object-fit: cover;
    }

    .mask {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        max-width: 100%;
        width: 60vw;
        height: 60vw;
        border-radius: 6px;
        outline: rgba(0, 0, 0, .25) solid 20vmax;

        &::before {
            content: '';
            display: block;
            position: absolute;
            top: 0;
            left: 50%;
            width: 120%;
            height: 3px;
            transform: translate(-50%);
            background-image: linear-gradient(to left, rgba(105, 120, 254, 0), rgba(105, 120, 254, 0.9) 50%, rgba(105, 120, 254, 0));
            filter: drop-shadow(10px 10px 10px rgba(105, 120, 254, 1));
            animation: translateYAnimation 4s linear infinite;
        }
    }
}

.Qqrcode {
    --m-overlay-bg-color: #0000000e;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值