uniapp的H5网页使用扫码(支持二维码与条形码)

技术支持(@zxing/library,webrtc-adapter)

运行条件必须是https

运行条件必须是https

运行条件必须是https

<template>
	<div class="page-scan" style="flex: 1; position: relative;">
		<!-- 扫码区域 -->
		<div class="QrCode" ref="videoContainer" style="height: 100%;">
			<!-- <video id="kankan" ref="video" height="100%" autoplay playsinline muted style="object-fit: cover;"></video> -->
		</div>

		<!-- 扫码样式遮罩 -->
		<div class="qr-scanner">
			<div class="box">
				<div class="line"></div>
				<div class="angle"></div>
			</div>
			<div class="mask1"></div>
			<div class="mask2"></div>
			<div class="mask3"></div>
			<div class="mask4"></div>
		</div>

	</div>
</template>

<script>
	import 'webrtc-adapter'
	import {
		BrowserMultiFormatReader
	} from '@zxing/library'

	export default {
		name: 'ScanCodePage',
		data() {
			return {
				codeReader: null,
				scanning: false,
				videoEl: null, // 保存原生 video 引用
			}
		},
		mounted() {
			this.codeReader = new BrowserMultiFormatReader()
			this.openScan()
		},
		beforeUnmount() {
			this.stopScan()
		},
		methods: {
			async openScan() {
				try {
					// 1. 创建原生 video 元素
					const video = document.createElement('video')
					video.setAttribute('id', 'kankan')
					video.setAttribute('autoplay', true)
					video.setAttribute('playsinline', true)
					video.setAttribute('muted', true)
					video.style.width = '100%'
					video.style.height = '100%'
					video.style.objectFit = 'cover'
					// 2. 挂载到页面上
					this.$refs.videoContainer.appendChild(video)
					this.videoEl = video

					// 3. 获取摄像头设备
					const devices = await this.codeReader.listVideoInputDevices()
					if (!devices.length) {
						// this.$toast.fail('未找到摄像头设备')
						return
					}

					let deviceId = devices[0].deviceId
					if (devices.length > 1) {
						const backCamera = devices.find(d => d.label.toLowerCase().includes('back'))
						if (backCamera) {
							deviceId = backCamera.deviceId
						} else {
							deviceId = devices[1].deviceId
						}
					}

					// 4. 启动扫码
					this.codeReader.reset()
					this.scanning = true
					
					this.codeReader.decodeFromInputVideoDeviceContinuously(
						deviceId,
						video,
						(result, err) => {
							uni.showToast({
								title: '扫码中',
								icon: 'none'
							})
							if (result) {
								uni.showToast({
									title: '扫码成功',
									icon: 'none'
								})
								this.stopScan()
							}
							if (err && err.name !== 'NotFoundException') {
								uni.showToast({
									title: '扫码失败',
									icon: 'none'
								})
								// this.$toast.fail(err.message || err)
							}
						}
					)
				} catch (error) {
					// this.$toast.fail(error.message || error)
				}
			},
			stopScan() {
				if (this.scanning) {
					this.codeReader.reset()
					this.scanning = false

					// 清理 video 元素
					if (this.videoEl && this.videoEl.parentNode) {
						this.videoEl.parentNode.removeChild(this.videoEl)
					}
					this.videoEl = null
				}
			},
		},
	}
</script>


<style scoped lang="scss">
	.page-scan {
		position: relative;
		height: 100vh;
		display: flex;
		flex-direction: column;
	}

	.QrCode {
		flex: 1;
		position: relative;
	}

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

	.qr-scanner {
		background-size: 3rem 3rem;
		background-position: -1rem -1rem;
		position: absolute;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		z-index: 9;
		width: 100%;
		height: 100%;
	}

	.qr-scanner .box {
		width: 75vw;
		height: 75vw;
		max-height: 75vh;
		max-width: 75vh;
		position: relative;
		left: 50%;
		top: 50%;
		transform: translate(-50%, -50%);
		overflow: hidden;
	}

	.qr-scanner .line {
		height: calc(100% - 2px);
		width: 100%;
		background: linear-gradient(180deg, rgba(0, 255, 51, 0) 43%, #3F90FD 211%);
		border-bottom: 3px solid #3F90FD;
		transform: translateY(-100%);
		animation: radar-beam 2s infinite;
		animation-timing-function: cubic-bezier(0.53, 0, 0.43, 0.99);
		animation-delay: 1.4s;
	}

	.qr-scanner .box:after,
	.qr-scanner .box:before,
	.qr-scanner .angle:after,
	.qr-scanner .angle:before {
		content: '';
		display: block;
		position: absolute;
		width: 3vw;
		height: 3vw;
		border: 0.2rem solid transparent;
	}

	.qr-scanner .box:after,
	.qr-scanner .box:before {
		top: 0;
		border-top-color: #3F90FD;
	}

	.qr-scanner .angle:after,
	.qr-scanner .angle:before {
		bottom: 0;
		border-bottom-color: #3F90FD;
	}

	.qr-scanner .box:before,
	.qr-scanner .angle:before {
		left: 0;
		border-left-color: #3F90FD;
	}

	.qr-scanner .box:after,
	.qr-scanner .angle:after {
		right: 0;
		border-right-color: #3F90FD;
	}

	@keyframes radar-beam {
		0% {
			transform: translateY(-100%);
		}

		100% {
			transform: translateY(0);
		}
	}

	.back-btn {
		position: fixed;
		top: 20px;
		left: 20px;
		padding: 6px 12px;
		background: rgba(0, 0, 0, 0.4);
		border: none;
		border-radius: 4px;
		color: white;
		font-size: 14px;
		z-index: 999;
		cursor: pointer;
	}

	.back-btn:hover {
		background: rgba(0, 0, 0, 0.6);
	}
</style>

由于uniapp不支持原生video标签,只能自己创建

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值