由于js是单线程,以及浏览器的键盘事件,最多只能做到关于Ctrl、Shift、Alt加任意一个的键位,无法做到同时使用方向键上和右,这在网页游戏中是致命的,所以在下实现了多重组合键的事件绑定

注意在使用组合键事件时,暂不支持声明Ctrl++这种按键,最好避免使用 ‘+’ 和 ‘‘ 的键位,因为 ‘+’ 作为连接组合键,’‘作为键盘事件的命名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
class KeyboardBinding {
// 声明一个索引,区分订阅事件
index: number
// 声明一个事件对象,以key, fn形式存储键盘事件
events: Record<string, (e: KeyboardEvent) => void>
// 声明一个数组,用于记录键盘已经按下的按键
keydownArr: string[]

// 初始化
constructor() {
this.index = 0
this.events = {}
this.keydownArr = []
// 键盘事件只需要初始化一次,不需要多次初始化
window.addEventListener('keydown', this._event.bind(this))
window.addEventListener('keypress', this._event.bind(this))
window.addEventListener('keyup', this._event.bind(this))
// 失去焦点,则清空keydownArr数组
window.addEventListener('blur', () => {
this.keydownArr = []
})
}

// 处理按键事件
_event(e: KeyboardEvent) {
if (e.type === 'keydown') {
// 当按键按下时,将键盘值记录到keydownArr数组中
if (this.keydownArr.findIndex((k: string) => k === e.key) < 0) {
this.keydownArr.push(e.key)
}
} else if (e.type === 'keyup') {
// 当按键松开时,将键盘值在keydownArr数组中移除
this.keydownArr = this.keydownArr.filter((k: string) => k !== e.key)
}

// 当前没有按键按下时,不处理事件
if (this.keydownArr.length <= 0) return

const keys = Object.keys(this.events)
// 没有注册键盘事件,不处理事件
if (keys.length <= 0) return

for (const key of keys) {
// 注册事件时,以 _ 区分,左侧为键盘值,右侧为索引
// 订阅按键事件时,以 + 来组合多按键
// 只需要判断key是否全部在keydownArr字典序中即可
// 需要先判断 multiKeyArr的长度是否与 keydownArr 的长度一致,如果不一致,则不处理事件
// 长度一致时,再去检测 multiKeyArr 中的键值是否都在 keydownArr中,如果是则处理事件
const multiKeyArr = key.substring(0, key.lastIndexOf('_')).split('+')
if (multiKeyArr.length === this.keydownArr.length) {
if (
multiKeyArr.filter((k) => this.keydownArr.includes(k)).length ===
this.keydownArr.length
) {
this.events[key](e)
break
}
}
}
}

// 订阅按键
subscribe(keyboard: string, eventFn: (e: KeyboardEvent) => void): string {
const key = `${keyboard}_${this.index++}`
this.events[key] = eventFn
return key
}

// 取消订阅
unsubscribe(key: string) {
if (Object.keys(this.events).filter((k) => k === key)[0]) {
delete this.events[key]
}
}
}

const keyboards: KeyboardBinding = bootstrap()

function bootstrap() {
return new KeyboardBinding()
}

export default keyboards

源码学习

Keyboard Bindings - CodePenhttps://codepen.io/EvilChan/pen/rNqJjvZ