web端 实现扫描二维码

1,689 阅读4分钟

环境:vue2 

"@zxing/library": "^0.19.1",

扫码方式

第一种:微信jssdk自带的扫一扫功能,优点:无兼容性问题,只要微信能扫一扫的场景,这个api都能扫。缺点:只能在微信环境使用,浏览器环境中不能使用,另外一点,需要后端配合出接口,获取当前公众号的appid等信息。

第二种:直接用原生插件实现,发现两种插件 vue-qrcode-reader 和 @zxing/library,两个插件都可以在项目中使用。

但是vue-qrcode-reader在苹果手机中无法使用,第一次扫码无反应调不起摄像头,需要返回第二次进入才会正常(暂未找到解决方法)

@zxing/library安卓ios都能正常调用起摄像头,但是对于华为手机,因为华为手机有6个摄像头,正常手机就2个摄像头,所以对于华为手机要兼容判断下。代码中已展示。

还有二维码如果参数太多,生成的二维码密度就会越多,正常大小扫不出来,所以需要放大二维码,参数不能传太多,影响扫码准确性。

zxing/library 库兼容问题

在装有 iOS < 14.3 相机访问仅适用于本机 Safari,而不适用于其他浏览器 (Chrome,…) 或使用 UIWebView 或 WKWebView 的应用。这不是这个库的限制,而是苹果有限的 WebRTC 支持的限制,但是 iOS 14.3(2020 年 12 月发布)现在也支持第三方浏览器中的 WebRTC。

浏览器层使用的是 MediaDevices Web API,这是旧版浏览器不支持的。

您可以使用 WebRTC 适配器等外部 polyfill 来增加浏览器兼容性。

另外,请注意,该库使用的是 TypedArray 等,这在较旧的浏览器(例如 Android 4 默认浏览器)中不可用。

您可以使用 core-js 添加对这些浏览器的支持。

@zxing/library 的 使用方法 ,可直接复制使用

  • 安装@zxing/library库:npm install @zxing/library
  • 在页面中点击某个按钮,然后进行路由跳转,跳转到扫码页面
<template>
	<div class="page-scan">
		<div class="scan-box">
			<video ref="video" id="video" class="scan-video" autoplay></video>
			<div class="qr-scanner">
				<div class="box">
					<div class="line"></div>
					<div class="angle"></div>
				</div>
			</div>
			<div class="scan-tip">{{ scanTextData.tipMsg }}</div>
		</div>
		<!-- <div @click.stop="destroyed">重置摄像头</div> -->
	</div>
</template>

<script>
import { BrowserMultiFormatReader } from '@zxing/library';
import { $verification } from '@/api';
export default {
	name: 'scanCodePage',
	data() {
		return {
			scanTextData: {
				codeReader: null,
				tipMsg: '扫描核销码',
				// 这个,就是当前调用的摄像头的索引,为什么是6,我会在后面说的 华为手机是鸿蒙系统有8个摄像头
				num: 5,
				// 这个就是扫描到的摄像头的数量
				videoLength: '',
			},
			hasBind: false,
		};
	},
	props: {
		show: {
			type: Boolean,
			default: false,
		},
	},
	mounted() {
		this.scanTextData.codeReader = new BrowserMultiFormatReader();
		this.openScan(); // 打开摄像头
	},
	watch: {
		show(val) {
			if (!val) {
				// 关闭摄像头
				if (!document.getElementById('video')) {
					alert('请授权');
					return;
				}
				let thisVideo = document.getElementById('video');
				thisVideo.srcObject.getTracks()[0].stop();
				this.scanTextData.codeReader.reset();
			} else {
				if (this.scanTextData.codeReader === null) {
					this.scanTextData.codeReader = new BrowserMultiFormatReader();
				}
				this.openScan();
			}
		},
	},
	methods: {
		async openScan() {
			this.scanTextData.codeReader
				.getVideoInputDevices()
				.then((videoInputDevices) => {
					// 默认获取第一个摄像头设备id
					let firstDeviceId = videoInputDevices[0].deviceId;
					console.log('手机摄像头的数量', videoInputDevices.length, videoInputDevices);
					// 获取第一个摄像头设备的名称
					const videoInputDeviceslablestr = JSON.stringify(videoInputDevices[0].label);
					if (videoInputDevices.length > 1) {
						// 华为手机有6个摄像头,前三个是前置,后三个是后置,第6个摄像头最清晰
						if (videoInputDevices.length > 5) {
							firstDeviceId = videoInputDevices[5].deviceId;
						} else {
							// 判断是否后置摄像头
							if (videoInputDeviceslablestr.indexOf('back') > -1) {
								firstDeviceId = videoInputDevices[0].deviceId;
							} else {
								firstDeviceId = videoInputDevices[1].deviceId;
							}
						}
					}
					this.decodeFromInputVideoFunc(firstDeviceId);
				})
				.catch((err) => {
					console.error(err);
				});
		},
		// 解析 二维码
		decodeFromInputVideoFunc(firstDeviceId) {
			this.scanTextData.codeReader.reset();
			this.scanTextData.codeReader.decodeFromInputVideoDeviceContinuously(
				firstDeviceId,
				'video',
				(result, err) => {
					if (result && result.text) {
						this.handleResult(result.text);
					}
					if (err && !err) {
						console.log(err);
						this.$toast.fail(err);
					}
				}
			);
		},
		// 解析二维码成功后 执行的回调 (逻辑处理或直接返回扫码结果)
		async handleResult(scanResult) {
			console.log('逻辑处理或直接返回扫码结果', scanResult);
			// TODO 逻辑处理或直接返回扫码结果
			// alert('解析二维码成功后 执行的回调 扫码结果:11111', scanResult);
			try {
				let res = await $verification({ code: scanResult });
				this.$router.push({
					name: 'write_off',
					query: { is: 2 },
					params: { order_detail: JSON.stringify(res.data) },
				});
			} catch (error) {
				console.log(error);
				this.$router.push({ name: 'write_off', query: { is: 3 } });
			}
		},
	},
	destroyed() {
		this.scanTextData.codeReader.reset(); // 重置摄像头
	},
};
</script>

<style lang="less" scoped>
.scan-box {
	position: fixed;
	top: 0;
	left: 0;
	height: 100%;
	width: 100vw;
	background-image: linear-gradient(
			0deg,
			transparent 24%,
			rgba(32, 255, 77, 0.1) 25%,
			rgba(32, 255, 77, 0.1) 26%,
			transparent 27%,
			transparent 74%,
			rgba(32, 255, 77, 0.1) 75%,
			rgba(32, 255, 77, 0.1) 76%,
			transparent 77%,
			transparent
		),
		linear-gradient(
			90deg,
			transparent 24%,
			rgba(32, 255, 77, 0.1) 25%,
			rgba(32, 255, 77, 0.1) 26%,
			transparent 27%,
			transparent 74%,
			rgba(32, 255, 77, 0.1) 75%,
			rgba(32, 255, 77, 0.1) 76%,
			transparent 77%,
			transparent
		);
	background-size: 3rem 3rem;
	background-position: -1rem -1rem;
}

.scan-video {
	height: 100vh;
	width: 100vw;
	object-fit: cover;
}

.qr-scanner .box {
	width: 213px;
	height: 213px;
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	overflow: hidden;
	border: 0.1rem solid rgba(0, 255, 51, 0.2);
	/* background: url('http://resource.beige.world/imgs/gongconghao.png') no-repeat center center; */
}

.qr-scanner .line {
	height: calc(100% - 2px);
	width: 100%;
	background: linear-gradient(180deg, rgba(0, 255, 51, 0) 43%, #00ff33 211%);
	border-bottom: 3px solid #00ff33;
	transform: translateY(-100%);
	animation: radar-beam 2s infinite alternate;
	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: #00ff33;
}

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

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

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

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

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

.scan-tip {
	width: 100vw;
	text-align: center;
	margin-bottom: 5vh;
	color: white;
	font-size: 5vw;
	position: absolute;
	bottom: 50px;
	left: 0;
	color: #fff;
}

.page-scan {
	overflow-y: hidden;
}
</style>

转载于www.cnblogs.com/cxx9759/p/1… 仅用于学习笔记,如侵删