最近遇到一个使用扫码枪输入的问题,本来是使用 Tauri 来开发成 PC 软件,但是因为页面是使用 Vue 3.0 开发,所以本质是个前端问题,遇到的问题主要就是避免人为键盘误触、避免中文输入法的干扰。
扫码枪的输入流程
目前市面上的扫码枪,都是扫码枪内置解码功能,所以输入流程大致是这样:
- 扫码枪扫码
- 扫码枪解码一维码/二维码
- 扫码枪模拟键盘输入
- 键盘输入的内容自动展示到输入区域
这里要解决的问题主要就是两个:
- 如果用户把输入法设置为中文输入法,如何避免异常?
- 如何区分是扫码枪输入还是人为输入?
解决方案
查询目前的解决方案,主要有以下几种:
ime-mode
属性
部分网上方案提示可以使用css的ime-mode
属性,主要有 5 个值:
auto
: 不更改现在输入法的状态,默认值。normal
: 输入法状态应该为正常,此值可在css中用于覆盖页面的设置。active
: 指定所有使用ime输入的字符。即激活本地语言输入法。用户仍可以撤销激活imeinactive
: 指定所有不使用ime输入的字符。即激活非本地语言。用户仍可以撤销激活imedisabled
: 完全禁用ime。对于有焦点的控件(如输入框),用户不可以激活ime。
所以当某个 input 文本域不需要输入中文的时候,就可以把ime-mode
设置为inactive
或disabled
。
但是该属性大部分浏览器都不支持,所以基本可以不用考虑。
password
方案
当把input
的类型设置为password
,并把focus到输入框,确实输入法会强制切换为英文模式。
但是此种方式输入的内容不可见,还可能会触发浏览器的自动填充功能,并且表单提交后,还会提示是否要保存密码。
<template>
<input id="code-input" type="password" autocomplete="off" autofocus />
</template>
<script setup lang="ts">
import { onMounted } from 'vue';
const focusInput = () => {
window.setTimeout(function () {
document.getElementById('code-input')?.focus();
}, 0);
}
onMounted(() => {
focusInput();
})
</script>
监听键盘输入事件获取输入
直接不放input框,通过监听键盘输入,来获取输入内容。
考虑到扫码枪输入很快,我们可以判断键盘输入间隔,如果小于 50ms,就认为是扫码枪输入,否则为人为输入。
没有使用keyup
事件是因为无法区分大小写状态。
<script setup lang="ts">
import { ref, onMounted } from 'vue';
const code = ref('');
interface TimeDiff {
lastTime: number; // 上一次按键时间
nextTime: number; // 这一次按键时间
}
let timeDiff: TimeDiff = {
lastTime: 0,
nextTime: 0,
};
const keyPressEvent = async (e: KeyboardEvent) => {
const keyCode = e.code;
const key = e.key;
// ascii 码可见字符范围为 33-126(不包含空格)
const minAsciiCode = 33;
const maxAsciiCode = 126;
timeDiff.nextTime = new Date().getTime();
const diff = timeDiff.nextTime - timeDiff.lastTime;
const machineDiff = 50;
// 如果两次键盘事件的间隔事件超出指定值,就认为是人为输入,直接清空,且不响应
if (diff > machineDiff) {
code.value = "";
timeDiff.lastTime = 0;
}
if (timeDiff.lastTime === 0 || diff < machineDiff) {
// 如果在可见字符范围内,那就拼接数据
if (key.length === 1 && key.charCodeAt(0) >= minAsciiCode && key.charCodeAt(0) <= maxAsciiCode) {
vode.value += key;
timeDiff.lastTime = timeDiff.nextTime;
}
if (keyCode === 'Enter' && diff < machineDiff) {
// 这里可以进行数据的提交
// ...
code.value = "";
timeDiff = {
lastTime: 0,
nextTime: 0,
};
}
}
};
onMounted(() => {
window.addEventListener('keypress', keyPressEvent);
})
</script>