完成键盘的开发
parent
d6ca6d7133
commit
6c907b4e6d
93
Readme.md
93
Readme.md
|
@ -1,16 +1,40 @@
|
||||||
## JS键盘热键
|
## JS键盘热键
|
||||||
> 支持各种按钮组合。
|
> 支持各种按钮组合。原生js开发, 无任何依赖(不到`2KB`)。使用也非常简单。
|
||||||
>> 1.0版 功能键(Shift, Ctrl, Alt/Option, Win/Cmd)不区分左右。
|
>> 1.0版 功能键(Shift, Ctrl, Alt/Option, Win/Cmd)不区分左右。
|
||||||
|
|
||||||
|
|
||||||
![keyboard](./keyboard.jpg)
|
![keyboard](./keyboard.jpg)
|
||||||
|
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
> 键盘
|
||||||
|
|
||||||
|
```js
|
||||||
|
import Keyboard from 'http://unpkg.bytedo.org/@bytedo/keyboard/dist/index.js'
|
||||||
|
|
||||||
|
var kb = new Keyboard()
|
||||||
|
|
||||||
|
// 同时按Ctrl键和C键, 触发回调
|
||||||
|
kb.on(['ctrl + c'], ev => {
|
||||||
|
// todo...
|
||||||
|
})
|
||||||
|
|
||||||
|
// 分别按下 Ctrl + C键, 然后再 300毫秒内按下Ctrl + V键, 触发回调
|
||||||
|
kb.on(['ctrl + c', 'ctrl + v'], ev => {
|
||||||
|
// todo...
|
||||||
|
})
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 辅助功能键
|
### 辅助功能键
|
||||||
> 辅助功能键, 不支持单独设置热键。
|
> 辅助功能键, 不支持单独设置热键。
|
||||||
>> 包括 `Ctrl、Shift、Alt/Option、Win/Cmd` 这4个。
|
>> 包括 `Ctrl、Shift、Alt/Option、Win/Cmd` 这4个。
|
||||||
|
|
||||||
## 普通按键
|
|
||||||
|
|
||||||
|
### 普通按键
|
||||||
> 即除了辅助功能键以外的其他按键。可以单独设置, 也可以配合辅助按键组合使用。
|
> 即除了辅助功能键以外的其他按键。可以单独设置, 也可以配合辅助按键组合使用。
|
||||||
>> 但是, 不允许在一组里出现多次。如需要, 请分组。
|
>> 但是, 不允许在一组里出现多次。如需要, 请分组。
|
||||||
|
|
||||||
|
@ -31,3 +55,68 @@ kb.on(['ctrl + c', 'ctrl + v'], ev => {
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
+ .on(actions<Array>, callback<Function>)
|
||||||
|
> 监听键盘动作组合, 支持单组或双组。
|
||||||
|
>> 双组时, 2组按键前后时差不能超过`300毫秒`, 否则视为2次独立的操作。
|
||||||
|
>> **键名不区分大小写**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
+ .off(actions<Array>, callback<Function>)
|
||||||
|
> 移除键盘监听。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
+ .destroy()
|
||||||
|
> 销毁整个键盘监听。
|
||||||
|
|
||||||
|
|
||||||
|
### 键名对照表
|
||||||
|
> 键名不区别大小写, 内部统一转为小写。
|
||||||
|
|
||||||
|
|
||||||
|
| 原始按键 | 修正后的键名 | 说明 |
|
||||||
|
| :-: | :-: | - |
|
||||||
|
| 0-9 | 0-9 | 数字键直接用阿拉伯数字, 不区别主键位和小数字键盘位 |
|
||||||
|
| A-Z | a-z | 字母键同样不变, 直接原样使用 |
|
||||||
|
| F1-F12 | f1-f12 | fn功能键,同样对应 |
|
||||||
|
| Prt | f13/print | 2种写法都可以 |
|
||||||
|
| Scr | f14/screen | 2种写法都可以 |
|
||||||
|
| Pau | f15/pause | 2种写法都可以 |
|
||||||
|
| left | left | 方向键 |
|
||||||
|
| right | right | 方向键 |
|
||||||
|
| up | up | 方向键 |
|
||||||
|
| down | down | 方向键 |
|
||||||
|
| Pg▴ | pageup | 向上翻页 |
|
||||||
|
| Pg▾ | pagedown | 向下翻页 |
|
||||||
|
| Home | home | Home键 |
|
||||||
|
| End | end | End键 |
|
||||||
|
| Ins | insert | 插入键 |
|
||||||
|
| Del | delete | 删除键(注意不是回退键) |
|
||||||
|
| Esc | esc | 退出键(左上角) |
|
||||||
|
| Menu | menu | 菜单键(部分键盘没有这个按键) |
|
||||||
|
| Caps | capslock | 大写锁定键(这个键的使用要特别注意) |
|
||||||
|
| Numlock | numlock | 数字键锁定键(87键以下的键盘没有) |
|
||||||
|
| Backspace | backspace | 回退键(=号键右边那个) |
|
||||||
|
| Tab | tab | 制表符键 |
|
||||||
|
| Cmd/Win | meta | Command/Win键(1.0版不区别左右) |
|
||||||
|
| Space | space | 空格键 |
|
||||||
|
| Ctrl | ctrl | Ctrl键(1.0版不区分左右) |
|
||||||
|
| Shift | shift | Shift键(1.0版不区分左右) |
|
||||||
|
| Alt/Option| alt | Alt/Option键(1.0版不区分左右) |
|
||||||
|
| Enter | enter | 回车键(1.0版不区分小数字键盘的回车) |
|
||||||
|
| ' | ' | 单引号键 |
|
||||||
|
| * | * | 小数字键盘中的乘号 |
|
||||||
|
| + | + | 小数字键盘中的加号 |
|
||||||
|
| - | - | 小数字键盘中的减号;及主键盘中的减号 (1.0版不区分) |
|
||||||
|
| / | / | 小数字键盘中的除号;及主键盘中的斜杠 (1.0版不区分) |
|
||||||
|
| 。 | . | 小数字键盘中的小数点;及主键盘中的斜句号 (1.0版不区分) |
|
||||||
|
| , | , | 逗号 |
|
||||||
|
| ; | ; | 分号 |
|
||||||
|
| = | = | 等号 |
|
||||||
|
| [ | [ | 左边中括号 |
|
||||||
|
| ] | ] | 右边中括号 |
|
||||||
|
| ` | ` | 反引号(Tab上面的键) |
|
||||||
|
| \ | \ | 反斜杠 |
|
227
src/index.es7
227
src/index.es7
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
import { KEY_DICT, MULTI_KEYS, MULTI_KEY_CODES } from './key.dict.js'
|
import { KEY_DICT, MULTI_KEYS, MULTI_KEY_CODES } from './key.dict.js'
|
||||||
|
|
||||||
var log = console.log
|
|
||||||
|
|
||||||
function bind(fn) {
|
function bind(fn) {
|
||||||
document.addEventListener('keydown', fn, false)
|
document.addEventListener('keydown', fn, false)
|
||||||
return fn
|
return fn
|
||||||
|
@ -17,47 +15,102 @@ function unbind(fn) {
|
||||||
document.removeEventListener('keydown', fn, false)
|
document.removeEventListener('keydown', fn, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hide(target, name, value) {
|
||||||
|
Object.defineProperty(target, name, {
|
||||||
|
value,
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按键检测
|
||||||
|
function check(ev) {
|
||||||
|
var checked = false
|
||||||
|
|
||||||
|
checked = ev.keyCode === this.keys.key
|
||||||
|
|
||||||
|
if (checked) {
|
||||||
|
for (let k of this.keys) {
|
||||||
|
checked = ev[`${k}Key`]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return checked
|
||||||
|
}
|
||||||
|
|
||||||
export default class Keyboard {
|
export default class Keyboard {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.__EVENTS__ = []
|
hide(this, '__EVENTS__', {})
|
||||||
this.__LAST__ = {}
|
|
||||||
this.__TIME__ = 0
|
|
||||||
|
|
||||||
this._keydown = bind(ev => {
|
hide(
|
||||||
|
this,
|
||||||
|
'_keydown',
|
||||||
|
bind(ev => {
|
||||||
if (MULTI_KEY_CODES.includes(ev.keyCode)) {
|
if (MULTI_KEY_CODES.includes(ev.keyCode)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let k in this.__EVENTS__) {
|
for (let k in this.__EVENTS__) {
|
||||||
var item = this.__EVENTS__[k]
|
var item = this.__EVENTS__[k]
|
||||||
var res = item.check(ev)
|
var res = item.check(ev)
|
||||||
|
var now = Date.now()
|
||||||
|
var end = false // 是否结束检测
|
||||||
|
|
||||||
if (res) {
|
// 假设之前有激活过, 优先再检测子组合
|
||||||
this.__LAST__[k]++
|
if (item.actived === true) {
|
||||||
|
// 如果超时(300毫秒)了, 则不再管子组合了
|
||||||
|
if (now - item.last > 300) {
|
||||||
|
delete item.actived
|
||||||
} else {
|
} else {
|
||||||
this.__LAST__[k] = -1
|
for (let next of item.next) {
|
||||||
}
|
let tmp = next.check(ev)
|
||||||
log('-------------------', res, this.__LAST__[k])
|
if (tmp) {
|
||||||
|
end = true
|
||||||
if (this.__LAST__[k] + 1 === item.dict.length) {
|
// 第2组一定有回调,无需判断
|
||||||
item.fn.forEach(function(fn) {
|
next.fn.forEach(function(fn) {
|
||||||
fn(ev)
|
fn(ev)
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第一组, 只要检测通过了, 就触发回调
|
||||||
|
if (res) {
|
||||||
|
end = true
|
||||||
|
if (item.next) {
|
||||||
|
item.actived = true
|
||||||
|
item.last = now
|
||||||
|
}
|
||||||
|
// 有回调就触发
|
||||||
|
if (item.fn) {
|
||||||
|
item.fn.forEach(function(fn) {
|
||||||
|
fn(ev)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 命中了, 后面的就不再检测了
|
||||||
|
if (end) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
delete this.__EVENTS__
|
|
||||||
unbind(this._keydown)
|
unbind(this._keydown)
|
||||||
|
delete this.__EVENTS__
|
||||||
|
delete this._keydown
|
||||||
}
|
}
|
||||||
|
|
||||||
on(act, callback) {
|
__parse_action__(act) {
|
||||||
var key = []
|
var keys = []
|
||||||
var dict = []
|
var dict = []
|
||||||
var passed = true // 语法检测通过
|
var passed = true // 语法检测通过
|
||||||
var _this = this
|
|
||||||
|
|
||||||
act.forEach(it => {
|
act.forEach(it => {
|
||||||
var tmp = []
|
var tmp = []
|
||||||
|
@ -81,45 +134,127 @@ export default class Keyboard {
|
||||||
}
|
}
|
||||||
return k
|
return k
|
||||||
})
|
})
|
||||||
key.push(it.join('+'))
|
keys.push(it.join('+'))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return { keys, dict, passed }
|
||||||
|
}
|
||||||
|
|
||||||
|
off(act, callback) {
|
||||||
|
if (act.length < 1 || typeof callback !== 'function') {
|
||||||
|
return console.error('无效热键或回调')
|
||||||
|
}
|
||||||
|
|
||||||
|
let { keys, dict, passed } = this.__parse_action__(act)
|
||||||
|
|
||||||
if (passed) {
|
if (passed) {
|
||||||
key = key.join(',')
|
let k1 = keys[0]
|
||||||
this.__LAST__[key] = -1
|
|
||||||
|
|
||||||
if (this.__EVENTS__[key]) {
|
if (keys.length === 1) {
|
||||||
this.__EVENTS__[key].fn.push(callback)
|
if (this.__EVENTS__[k1] && this.__EVENTS__[k1].fn) {
|
||||||
|
let i = 0
|
||||||
|
for (let fn of this.__EVENTS__[k1].fn) {
|
||||||
|
if (fn === callback) {
|
||||||
|
this.__EVENTS__[k1].fn.splice(i, 1)
|
||||||
|
// 只剩1个, 删除fn字段
|
||||||
|
if (this.__EVENTS__[k1].fn.length < 1) {
|
||||||
|
delete this.__EVENTS__[k1].fn
|
||||||
|
|
||||||
|
// 删完fn字段, 如果没有next字段的话, 整个热键删除
|
||||||
|
if (!this.__EVENTS__[k1].next) {
|
||||||
|
delete this.__EVENTS__[k1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.__EVENTS__[key] = {
|
if (this.__EVENTS__[k1] && this.__EVENTS__[k1].next) {
|
||||||
key,
|
let k2 = keys[1]
|
||||||
dict,
|
let i = 0
|
||||||
check(ev) {
|
let sub
|
||||||
var now = Date.now()
|
|
||||||
var idx = _this.__LAST__[this.key]
|
|
||||||
var action = this.dict[idx + 1]
|
|
||||||
var checked = false
|
|
||||||
|
|
||||||
if (action) {
|
for (sub of this.__EVENTS__[k1].next) {
|
||||||
checked = ev.keyCode === action.key
|
if (sub.id === k2) {
|
||||||
|
break
|
||||||
if (idx > -1) {
|
|
||||||
if (now - _this.__TIME__ > 100) {
|
|
||||||
checked = false
|
|
||||||
}
|
}
|
||||||
}
|
i++
|
||||||
if (checked) {
|
|
||||||
for (let k of action) {
|
|
||||||
checked = ev[`${k}Key`]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log(action, now, checked)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return checked
|
if (sub.fn) {
|
||||||
},
|
let j = 0
|
||||||
|
for (let fn of sub.fn) {
|
||||||
|
if (fn === callback) {
|
||||||
|
sub.fn.splice(j, 1)
|
||||||
|
// 如果该组的事件为空了, 则删除该组合
|
||||||
|
if (sub.fn.length < 1) {
|
||||||
|
this.__EVENTS__[k1].next.splice(i, 1)
|
||||||
|
|
||||||
|
// 第2组, 如果回调队列为空了, 直接删除整个第2组
|
||||||
|
if (this.__EVENTS__[k1].next.length < 1) {
|
||||||
|
delete this.__EVENTS__[k1].next
|
||||||
|
|
||||||
|
// 如果此时, 上一层没的回调了, 则整个热键删除
|
||||||
|
if (!this.__EVENTS__[k1].fn) {
|
||||||
|
delete this.__EVENTS__[k1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
on(act, callback) {
|
||||||
|
if (act.length < 1 || typeof callback !== 'function') {
|
||||||
|
return console.error('无效热键或回调')
|
||||||
|
}
|
||||||
|
|
||||||
|
let { keys, dict, passed } = this.__parse_action__(act)
|
||||||
|
|
||||||
|
if (passed) {
|
||||||
|
// 最多支持2组
|
||||||
|
let first = keys.shift()
|
||||||
|
let second = keys.shift()
|
||||||
|
|
||||||
|
if (!this.__EVENTS__[first]) {
|
||||||
|
this.__EVENTS__[first] = {
|
||||||
|
id: first,
|
||||||
|
keys: dict[0],
|
||||||
|
last: 0, //上次触发时间戳
|
||||||
|
check
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (second) {
|
||||||
|
if (this.__EVENTS__[first].next) {
|
||||||
|
for (let i = -1, it; (it = this.__EVENTS__[first].next[++i]); ) {
|
||||||
|
if (it.id === second) {
|
||||||
|
it.fn.push(callback)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.__EVENTS__[first].next = [
|
||||||
|
{
|
||||||
|
id: second,
|
||||||
|
keys: dict[1],
|
||||||
|
check,
|
||||||
fn: [callback]
|
fn: [callback]
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
if (this.__EVENTS__[first].fn) {
|
||||||
|
this.__EVENTS__[first].fn.push(callback)
|
||||||
|
} else {
|
||||||
|
this.__EVENTS__[first].fn = [callback]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,8 +69,8 @@ export const KEY_DICT = {
|
||||||
backspace: 8,
|
backspace: 8,
|
||||||
numlock: 12,
|
numlock: 12,
|
||||||
capslock: 20,
|
capslock: 20,
|
||||||
escape: 27,
|
esc: 27,
|
||||||
contextmenu: 93,
|
menu: 93,
|
||||||
insert: 45,
|
insert: 45,
|
||||||
delete: 46,
|
delete: 46,
|
||||||
pagedown: 34,
|
pagedown: 34,
|
||||||
|
@ -95,5 +95,8 @@ export const KEY_DICT = {
|
||||||
f12: 123,
|
f12: 123,
|
||||||
f13: 124, // Print键
|
f13: 124, // Print键
|
||||||
f14: 125, // Screen键
|
f14: 125, // Screen键
|
||||||
f15: 126 // Pause键
|
f15: 126, // Pause键
|
||||||
|
print: 124, // Print键
|
||||||
|
screen: 125, // Screen键
|
||||||
|
pause: 126 // Pause键
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue