修改扫码时间间隔分辨率以及添加语音播报,对边框进行缩减由210px修改为180px
This commit is contained in:
@ -12,7 +12,7 @@
|
||||
<div class="focus-tip success" v-if="isPaused">
|
||||
<div class="scan-text-success">
|
||||
<el-icon><CircleCheckFilled /></el-icon>
|
||||
扫描成功,3秒后继续...
|
||||
扫描成功,2秒后继续...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -50,6 +50,43 @@ const zoomMin = ref(1)
|
||||
const zoomMax = ref(5)
|
||||
const currentZoom = ref(1)
|
||||
|
||||
// 音频上下文
|
||||
let audioCtx: AudioContext | null = null;
|
||||
|
||||
// 提示音播放函数
|
||||
const playBeep = () => {
|
||||
try {
|
||||
const AudioContext = window.AudioContext || (window as any).webkitAudioContext;
|
||||
if (!AudioContext) return;
|
||||
|
||||
if (!audioCtx) {
|
||||
audioCtx = new AudioContext();
|
||||
}
|
||||
|
||||
if (audioCtx.state === 'suspended') {
|
||||
audioCtx.resume();
|
||||
}
|
||||
|
||||
const oscillator = audioCtx.createOscillator();
|
||||
const gainNode = audioCtx.createGain();
|
||||
|
||||
oscillator.connect(gainNode);
|
||||
gainNode.connect(audioCtx.destination);
|
||||
|
||||
// 三角波,清脆响亮
|
||||
oscillator.type = 'triangle';
|
||||
oscillator.frequency.value = 1500;
|
||||
|
||||
gainNode.gain.setValueAtTime(1.0, audioCtx.currentTime);
|
||||
|
||||
oscillator.start();
|
||||
oscillator.stop(audioCtx.currentTime + 0.1);
|
||||
|
||||
} catch (e) {
|
||||
console.error("播放提示音失败:", e);
|
||||
}
|
||||
};
|
||||
|
||||
const startScanning = async () => {
|
||||
try {
|
||||
html5QrCode = new Html5Qrcode(scannerElementId, {
|
||||
@ -63,16 +100,16 @@ const startScanning = async () => {
|
||||
|
||||
const config = {
|
||||
fps: 20,
|
||||
// ★★★ 核心修改点 2:移除了 qrbox 属性 ★★★
|
||||
// 移除后,库默认会对每一帧的“全画面”进行解析,不再局限于中间区域
|
||||
// qrbox: { width: 300, height: 100 },
|
||||
|
||||
disableFlip: false,
|
||||
videoConstraints: {
|
||||
facingMode: "environment",
|
||||
// 保持高分辨率以支持微小条码
|
||||
width: { min: 1280, ideal: 3840, max: 3840 },
|
||||
height: { min: 720, ideal: 2160, max: 2160 },
|
||||
// ★★★ 核心修改:设置为 2K (QHD) 分辨率 ★★★
|
||||
// min: 1280x720 (保证低端机能启动)
|
||||
// ideal: 2560x1440 (2K QHD,清晰度与性能的平衡点)
|
||||
width: { min: 1280, ideal: 2560, max: 3840 },
|
||||
height: { min: 720, ideal: 1440, max: 2160 },
|
||||
// 16:9 的比例
|
||||
aspectRatio: { ideal: 1.7777777778 },
|
||||
focusMode: "continuous",
|
||||
advanced: [{ focusMode: "macro" }, { zoom: 2.0 }]
|
||||
}
|
||||
@ -84,14 +121,18 @@ const startScanning = async () => {
|
||||
(decodedText) => {
|
||||
if (isPaused.value) return
|
||||
console.log(`Scan: ${decodedText}`)
|
||||
|
||||
isPaused.value = true
|
||||
|
||||
playBeep();
|
||||
|
||||
emit('decode', decodedText)
|
||||
|
||||
if (navigator.vibrate) navigator.vibrate(200);
|
||||
|
||||
setTimeout(() => {
|
||||
isPaused.value = false
|
||||
}, 3000)
|
||||
}, 2000)
|
||||
},
|
||||
(errorMessage) => {
|
||||
// ignore
|
||||
@ -103,12 +144,14 @@ const startScanning = async () => {
|
||||
} catch (err: any) {
|
||||
let msg = '无法启动摄像头'
|
||||
console.error("Scanner Error:", err)
|
||||
if (err.name === 'OverconstrainedError') {
|
||||
msg = '摄像头不支持 2K 分辨率,请尝试降低配置'
|
||||
}
|
||||
errorMsg.value = msg
|
||||
emit('error', msg)
|
||||
}
|
||||
}
|
||||
|
||||
// 检测硬件变焦能力
|
||||
const checkZoomCapability = () => {
|
||||
if (!html5QrCode) return
|
||||
|
||||
@ -130,7 +173,6 @@ const checkZoomCapability = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 处理滑块拖动
|
||||
const handleZoom = () => {
|
||||
if (!html5QrCode) return
|
||||
|
||||
@ -154,9 +196,19 @@ const stopScanning = async () => {
|
||||
console.error("Stop failed", e)
|
||||
}
|
||||
}
|
||||
|
||||
if (audioCtx) {
|
||||
audioCtx.close();
|
||||
audioCtx = null;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
try {
|
||||
const AudioContext = window.AudioContext || (window as any).webkitAudioContext;
|
||||
if (AudioContext) audioCtx = new AudioContext();
|
||||
} catch(e) {}
|
||||
|
||||
setTimeout(() => {
|
||||
startScanning()
|
||||
}, 500)
|
||||
@ -178,7 +230,6 @@ onUnmounted(() => {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
/* 如果是全屏模式,这里不需要圆角,或者保持圆角视你的UI设计而定 */
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
@ -214,14 +265,12 @@ onUnmounted(() => {
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
/* --- ★ 修改点 3:视觉层 CSS 更新 --- */
|
||||
.focus-tip {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* 移除了 border 和 box-shadow,不再显示红框和黑色遮罩 */
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
@ -230,23 +279,21 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.focus-tip.success {
|
||||
background: rgba(103, 194, 58, 0.2); /* 成功时全屏微微泛绿 */
|
||||
background: rgba(103, 194, 58, 0.2);
|
||||
}
|
||||
|
||||
/* 扫描线改为全屏宽度 */
|
||||
.scan-line {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: rgba(255, 0, 0, 0.5);
|
||||
box-shadow: 0 0 4px rgba(255, 0, 0, 0.8);
|
||||
position: absolute;
|
||||
/* 动画范围从 10% 到 90% */
|
||||
animation: scan-move 2.5s infinite linear;
|
||||
}
|
||||
|
||||
.scan-text {
|
||||
position: absolute;
|
||||
bottom: 150px; /* 调整文字位置 */
|
||||
bottom: 150px;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 14px;
|
||||
text-shadow: 0 1px 3px rgba(0,0,0,0.8);
|
||||
@ -275,7 +322,6 @@ onUnmounted(() => {
|
||||
100% { top: 100%; opacity: 0; }
|
||||
}
|
||||
|
||||
/* 变焦控制器 */
|
||||
.zoom-control {
|
||||
position: absolute;
|
||||
bottom: 80px;
|
||||
|
||||
@ -22,7 +22,7 @@ import AppMain from './components/AppMain.vue'
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
width: 210px; /* 固定侧边栏宽度 */
|
||||
width: 180px; /* 固定侧边栏宽度 */
|
||||
height: 100%;
|
||||
background-color: #304156; /* 侧边栏背景色 */
|
||||
flex-shrink: 0; /* 防止被挤压 */
|
||||
@ -37,7 +37,7 @@ import AppMain from './components/AppMain.vue'
|
||||
flex-direction: column;
|
||||
overflow-y: auto; /* 关键:页面内容过多时,只在右侧区域滚动 */
|
||||
background-color: #f0f2f5; /* 右侧灰色背景,让白色卡片更明显 */
|
||||
padding: 20px; /* 给内部页面留出边距 */
|
||||
padding: 10px; /* 给内部页面留出边距 */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user