公司项目要求实现H5页面可以扫描快递单条形码并回显快递单号,于是用html5-qrcode库实现条形码扫描功能。
关于 html5-qrcode
html5-qrcode
是一个基于 HTML5 和 JavaScript 的开源库,它利用浏览器的MediaDevices.getUserMedia()
API 访问设备摄像头,实时扫描二维码(QR Code)和条形码(Barcode),无需安装插件或 App 。
html5-qrcode
是一个轻量、高效、纯前端的 二维码/条形码扫描库,专为在网页(H5)中实现摄像头扫码功能而设计。
GitHub 仓库:github.com/mebjas/html...
具体实现
实现效果:\
项目基于vue3框架开发:
1.安装html5-qrcode:
npm install html5-qrcode
2.页面代码实现:
html
<template>
<div class="registration-container">
<h2>快递登记</h2>
<el-form label-position="top" :model="ruleForm" :rules="rules" ref="ruleFormRef">
<el-form-item label="真实退回快递单号" prop="expressNumber">
<el-input v-model="ruleForm.expressNumber" placeholder="请输入快递单号" clearable>
<template #suffix>
<SvgIcon @click="startScan" name="take-pictures" width="24" height="24" />
</template>
</el-input>
</el-form-item>
<el-form-item label="快递公司" prop="expressCompany">
<el-input v-model="ruleForm.expressCompany" placeholder="请输入快递公司" clearable />
</el-form-item>
</el-form>
<div class="footer">
<el-button class="submit-btn" type="primary" style="width: 100%" @click="submitForm">提交</el-button>
</div>
</div>
<!-- 全屏扫码层 -->
<div v-if="scanning" class="scanner-container">
<div id="qr-reader" ref="qrReaderRef"></div>
<p class="scan-hint">请将快递单条形码对准扫描框</p>
<el-button class="close-btn" @click="stopScan">关闭</el-button>
</div>
</template>
<script lang="ts" setup>
import { ref, onUnmounted, reactive } from 'vue'
import { Html5Qrcode } from 'html5-qrcode'
import { type FormInstance, type FormRules, ElMessage } from 'element-plus'
defineOptions({
name: 'after-sales-registration'
})
const ruleFormRef = ref<FormInstance>()
const ruleForm = reactive({
expressNumber: '',
expressCompany: ''
})
const rules = reactive<FormRules>({
expressNumber: [{ required: true, message: '请输入快递单号', trigger: 'blur' }],
expressCompany: [{ required: true, message: '请输入快递公司', trigger: 'blur' }]
})
const scanning = ref(false)
const qrReaderRef = ref(null)
let html5QrCode: Html5Qrcode | null = null
// 开始扫描
const startScan = (event: Event) => {
// 阻止事件冒泡和默认行为,防止输入框获得焦点
event.preventDefault()
event.stopPropagation()
if (scanning.value) return
scanning.value = true
// 如果有活动的输入框,尝试让它失去焦点
if (document.activeElement instanceof HTMLElement) {
document.activeElement.blur()
}
// 延迟确保 DOM 渲染
setTimeout(() => {
try {
html5QrCode = new Html5Qrcode('qr-reader')
const config = {
fps: 10,
qrbox: { width: 300, height: 100 }, // 横向长条,适合条形码
formats: [
'code_128', // ✅ 快递单最常用
'code_39', // ✅ 部分快递使用
'ean_13' // ✅ 商品类快递
]
}
html5QrCode
.start(
{ facingMode: 'environment' }, // 后置摄像头
config,
(decodedText) => {
// console.log('🎉 识别到快递单号:', decodedText)
ruleForm.expressNumber = decodedText
stopScan() // 自动关闭
},
() => {}
)
.catch((err) => {
ElMessage.error('摄像头启动失败:' + (err.message || '未知错误'))
scanning.value = false
stopScan()
})
} catch (err) {
ElMessage.error(`扫码库初始化失败,请重试${err}`)
stopScan()
}
}, 300)
}
// 停止扫描
const stopScan = () => {
if (html5QrCode) {
html5QrCode
.stop()
.then(() => {
html5QrCode?.clear()
html5QrCode = null
scanning.value = false
})
.catch(() => {
// console.error('停止失败:', err)
scanning.value = false
})
} else {
scanning.value = false
}
}
const submitForm = () => {
ruleFormRef.value?.validate((valid) => {
if (valid) {
// TODO:提交事件
} else {
ElMessage.error('请填写完整信息')
}
})
}
// 组件卸载时确保关闭扫描
onUnmounted(() => {
if (html5QrCode) {
html5QrCode.stop()
}
})
</script>
<style lang="scss" scoped>
.registration-container {
width: 100%;
height: 100%;
padding: 20px;
background: #f8f8fa;
box-sizing: border-box;
.footer {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
padding: 17px 0;
text-align: center;
box-shadow: 0px -4px 16px 0px rgba(60, 126, 254, 0.2);
box-sizing: border-box;
.submit-btn {
width: 50% !important;
background: #3c7efe;
border-radius: 555px;
}
}
}
input {
width: 100%;
padding: 12px;
margin: 10px 0;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 16px;
box-sizing: border-box;
text-align: center;
background-color: #f9f9f9;
}
button {
width: 100%;
padding: 12px;
background-color: #d32f2f;
color: white;
border: none;
border-radius: 6px;
font-size: 16px;
cursor: pointer;
}
button:disabled {
background-color: #ef5350;
cursor: not-allowed;
}
button:hover:not(:disabled) {
background-color: #c62828;
}
/* 全屏扫码层 */
.scanner-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: black;
z-index: 9999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding-top: 40px;
}
#qr-reader {
width: 100%;
max-width: 500px;
border-radius: 12px;
overflow: hidden;
}
.scan-hint {
color: #4caf50;
margin-top: 12px;
font-size: 18px;
font-weight: bold;
}
.close-btn {
margin-top: 12px;
padding: 8px 16px;
background: #ff5252;
color: white;
border: none;
border-radius: 4px;
font-size: 14px;
}
</style>
其他
注意事项
- 必须 HTTPS:生产环境必须部署在 HTTPS 域名下。
- 用户授权:首次使用需用户允许摄像头权限。
- 性能优化:避免在低性能设备上长时间运行。
- 兼容性问题:在某些旧版浏览器可能不支持。
- 移动端体验:html5-qrcode 扫码体验一般,需要贴紧条形码。
其他方案对比
方案 | 技术栈 | 是否推荐 | 说明 |
---|---|---|---|
1. html5-qrcode + 格式过滤 |
纯前端 JS | ✅✅✅ 强烈推荐 | 轻量、易用、支持条形码 |
2. ZXing JS (原生) |
纯前端 JS | ✅ 推荐 | 功能强大,但配置复杂 |
3. 原生 getUserMedia + QuaggaJS |
纯前端 JS | ⚠️ 可用但不推荐 | QuaggaJS 已停止维护 |
4. 调用微信 JS-SDK 扫码 | 微信内置 | ✅ 限微信环境 | 依赖微信 App |
5. 调用 App 原生能力(Hybrid) | WebView + Native | ✅ 高性能 | 需开发 App |
6. 拍照上传 + 后端识别 | 前端 + 后端 | ✅ 稳定兜底 | 适合弱网或兼容性差的设备 |