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