业务背景
uniapp框架,需要兼容微信小程序和H5环境,实现扫描条形码,再根据扫描的结果跳转到指定页面的功能。 uni.scanCode(OBJECT)支持小程序等,但是不兼容H5。
由于只需要扫描识别128类型的条形码,因此选择quagga2这个专门支持条形码的开源库来实现。 使用缺点:浏览器端,上传的二维码图片,需要将二维码部分框选出来。
实现思路
- 引入Quagga2库
- 根据环境选择图片来源: 移动端直接拍照识别,浏览器端通过上传图片来识别
- 识别条形码
- 处理识别结果
完整代码
js
npm install @ericblade/quagga2
js
<template>
<view class="container">
<view v-if="isScanning" class="scanning-container">
<view id="scanner-container" class="scanner-view"></view>
<button @click="stopScan" class="stop-button">停止扫描</button>
</view>
</view>
</template>
<script>
import Quagga from "@ericblade/quagga2";
export default {
data() {
return {
isScanning: false,
isMobile: false
};
},
onLoad() {
this.checkEnvironment();
},
methods: {
// 检查设备类型
async checkEnvironment() {
try {
const ua = navigator.userAgent.toLowerCase();
this.isMobile = /mobile|android|iphone|ipad|phone/i.test(ua);
// 判断当前环境: 移动端使用摄像头实时扫描,浏览器端使用图片上传
if (this.isMobile) {
await this.startCameraScan();
} else {
uni.showModal({
content: "为了便于识别,请确保上传图片中,已经将条形码区域框选出来",
title: "提示",
success: async (res) => {
if (res.confirm) {
await this.uploadImageScan();
} else uni.navigateBack();
}
});
}
} catch (error) {
console.log("扫描启动失败", err);
this.checkFail();
}
},
// 启动摄像头扫描(移动端)
async startCameraScan() {
this.isScanning = true;
// 等待视图更新
await this.$nextTick();
return new Promise((resolve, reject) => {
Quagga.init(
{
inputStream: {
name: "Live",
type: "LiveStream",
target: document.querySelector("#scanner-container")
},
decoder: {
readers: ["code_128_reader"]//此处可继续添加支持的其他类型条形码,如:code_39_reader
},
locate: true,
numOfWorkers: 2
},
(err) => {
if (err) {
console.error("Quagga初始化失败:", err);
this.isScanning = false;
this.checkFail();
reject(err);
return;
}
Quagga.start();
resolve();
}
);
});
},
// 图片上传扫描(浏览器端)
async uploadImageScan() {
try {
const [file] = await new Promise((resolve, reject) => {
uni.chooseImage({
count: 1,
sizeType: ["original"],
sourceType: ["album"],
success: (res) => resolve(res.tempFiles),
fail: reject
});
});
// 识别图片中的条形码
const result = await new Promise((resolve, reject) => {
Quagga.decodeSingle(
{
decoder: {
readers: ["code_128_reader"]
},
locate: true,
src: file.path
},
(result) => {
if (result && result.codeResult) {
resolve(result);
} else {
this.checkFail();
reject(new Error("未识别到条形码"));
}
}
);
});
this.handleScanResult(result);
} catch (error) {
console.error("图片识别失败:", error);
this.checkFail();
}
},
// 处理识别结果
handleScanResult(result) {
const code = result.codeResult.code;
console.log("识别结果:", code)
},
//识别失败后的处理,返回上一页
checkFail() {
uni.navigateBack();
setTimeout(() => {
uni.showToast({
title: "识别失败,请重试",
icon: "error",
duration: 2000
});
}, 1);
},
// 停止扫描
stopScan() {
if (this.isScanning && Quagga) {
Quagga.stop();
this.isScanning = false;
}
}
},
// 页面卸载时清理资源
onUnload() {
this.stopScan();
}
};
</script>
<style lang="scss" scoped>
.container {
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
.stop-button {
margin: 10px 0;
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
background-color: #ff3b30;
color: white;
}
.scanner-view {
width: 100%;
height: 300px;
border: 2px solid #007aff;
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
}
}
</style>