feat: 1.版本升级

tujiawei 2023-10-09 19:40:19 +08:00
parent 1aebd6d839
commit 6556d213c1
11 changed files with 667 additions and 302 deletions

228
src/Keyboard.v1.js Normal file
View File

@ -0,0 +1,228 @@
/**
*
* @author yutent<yutent.io@gmail.com>
* @date 2020/10/29 16:48:26
*/
import { MULTI_KEY_CODES } from './key.dict.v1.js'
import { bind, unbind, hide, check, parseAction } from './utils';
class KeyboardV1 {
constructor(elem) {
this.$elem = elem
hide(this, '__EVENTS__', {})
hide(this, 'paused', false)
hide(this, '__finally__', null)
hide(
this,
'_keydown',
bind(elem, ev => {
// 允许暂停
if (this.paused) {
return
}
// 屏蔽纯辅助键的监听
if (MULTI_KEY_CODES.includes(ev.keyCode)) {
return
}
for (let k in this.__EVENTS__) {
var item = this.__EVENTS__[k]
var res = item.check(ev)
var now = Date.now()
var end = false // 是否结束检测
// 假设之前有激活过, 优先再检测子组合
if (item.actived === true) {
// 如果超时(300毫秒)了, 则不再管子组合了
if (now - item.last > 300) {
delete item.actived
} else {
for (let next of item.next) {
let tmp = next.check(ev)
if (tmp) {
end = true
// 第2组一定有回调,无需判断
next.fn.forEach(function(fn) {
fn(ev)
})
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
}
}
if (this.__finally__) {
this.__finally__(ev)
}
})
)
}
get disabled() {
return this.paused
}
set disabled(v) {
this.paused = !!v
}
destroy() {
unbind(this.$elem, this._keydown)
delete this.__EVENTS__
delete this._keydown
}
off(act, callback) {
if (act.length < 1 || typeof callback !== 'function') {
return console.error('无效热键或回调')
}
let { keys, dict, passed } = parseAction(act)
if (passed) {
let k1 = keys[0]
if (keys.length === 1) {
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 {
if (this.__EVENTS__[k1] && this.__EVENTS__[k1].next) {
let k2 = keys[1]
let i = 0
let sub
for (sub of this.__EVENTS__[k1].next) {
if (sub.id === k2) {
break
}
i++
}
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 } = parseAction(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]
}
]
} else {
if (this.__EVENTS__[first].fn) {
this.__EVENTS__[first].fn.push(callback)
} else {
this.__EVENTS__[first].fn = [callback]
}
}
}
}
finally(callback) {
if (typeof callback === 'function') {
this.__finally__ = callback
}
}
}
export { KeyboardV1 };

228
src/Keyboard.v2.js Normal file
View File

@ -0,0 +1,228 @@
/**
*
* @author yutent<yutent.io@gmail.com>
* @date 2020/10/29 16:48:26
*/
import { MULTI_KEY_CODES } from './key.dict.v2.js'
import { bind, unbind, hide, check, parseAction } from './utils';
class KeyboardV2 {
constructor(elem) {
this.$elem = elem
hide(this, '__EVENTS__', {})
hide(this, 'paused', false)
hide(this, '__finally__', null)
hide(
this,
'_keydown',
bind(elem, ev => {
// 允许暂停
if (this.paused) {
return
}
// 屏蔽纯辅助键的监听
if (MULTI_KEY_CODES.includes(ev.keyCode)) {
return
}
for (let k in this.__EVENTS__) {
var item = this.__EVENTS__[k]
var res = item.check(ev)
var now = Date.now()
var end = false // 是否结束检测
// 假设之前有激活过, 优先再检测子组合
if (item.actived === true) {
// 如果超时(300毫秒)了, 则不再管子组合了
if (now - item.last > 300) {
delete item.actived
} else {
for (let next of item.next) {
let tmp = next.check(ev)
if (tmp) {
end = true
// 第2组一定有回调,无需判断
next.fn.forEach(function(fn) {
fn(ev)
})
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
}
}
if (this.__finally__) {
this.__finally__(ev)
}
})
)
}
get disabled() {
return this.paused
}
set disabled(v) {
this.paused = !!v
}
destroy() {
unbind(this.$elem, this._keydown)
delete this.__EVENTS__
delete this._keydown
}
off(act, callback) {
if (act.length < 1 || typeof callback !== 'function') {
return console.error('无效热键或回调')
}
let { keys, dict, passed } = parseAction(act)
if (passed) {
let k1 = keys[0]
if (keys.length === 1) {
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 {
if (this.__EVENTS__[k1] && this.__EVENTS__[k1].next) {
let k2 = keys[1]
let i = 0
let sub
for (sub of this.__EVENTS__[k1].next) {
if (sub.id === k2) {
break
}
i++
}
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 } = parseAction(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]
}
]
} else {
if (this.__EVENTS__[first].fn) {
this.__EVENTS__[first].fn.push(callback)
} else {
this.__EVENTS__[first].fn = [callback]
}
}
}
}
finally(callback) {
if (typeof callback === 'function') {
this.__finally__ = callback
}
}
}
export { KeyboardV2 };

View File

@ -1,302 +0,0 @@
/**
*
* @author yutent<yutent.io@gmail.com>
* @date 2020/10/29 16:48:26
*/
import { KEY_DICT, MULTI_KEYS, MULTI_KEY_CODES } from './key.dict.js'
function bind(el, fn) {
;(el || document).addEventListener('keydown', fn, false)
return fn
}
function unbind(el, fn) {
;(el || 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
if (typeof this.keys.key === 'object') {
checked = this.keys.key.includes(ev.keyCode)
} else {
checked = ev.keyCode === this.keys.key
}
if (checked) {
for (let k in this.keys) {
if (ev[k] !== this.keys[k]) {
checked = false
break
}
}
}
return checked
}
export default class Keyboard {
constructor(elem) {
this.$elem = elem
hide(this, '__EVENTS__', {})
hide(this, 'paused', false)
hide(this, '__finally__', null)
hide(
this,
'_keydown',
bind(elem, ev => {
// 允许暂停
if (this.paused) {
return
}
// 屏蔽纯辅助键的监听
if (MULTI_KEY_CODES.includes(ev.keyCode)) {
return
}
for (let k in this.__EVENTS__) {
var item = this.__EVENTS__[k]
var res = item.check(ev)
var now = Date.now()
var end = false // 是否结束检测
// 假设之前有激活过, 优先再检测子组合
if (item.actived === true) {
// 如果超时(300毫秒)了, 则不再管子组合了
if (now - item.last > 300) {
delete item.actived
} else {
for (let next of item.next) {
let tmp = next.check(ev)
if (tmp) {
end = true
// 第2组一定有回调,无需判断
next.fn.forEach(function(fn) {
fn(ev)
})
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
}
}
if (this.__finally__) {
this.__finally__(ev)
}
})
)
}
get disabled() {
return this.paused
}
set disabled(v) {
this.paused = !!v
}
destroy() {
unbind(this.$elem, this._keydown)
delete this.__EVENTS__
delete this._keydown
}
__parse_action__(act) {
var keys = []
var dict = []
var passed = true // 语法检测通过
act.forEach(it => {
var tmp = {}
it = it.split('+').map(k => {
k = k.trim().toLowerCase()
if (MULTI_KEYS[k]) {
if (tmp[`${k}Key`]) {
passed = false
console.error('功能键,同组中不能重复。⎣%s⎤', it)
} else {
tmp[`${k}Key`] = true
}
} else {
if (tmp.key) {
passed = false
console.error('非功能键,同组不能出现多个。⎣%s⎤', it)
} else {
hide(tmp, 'key', KEY_DICT[k])
}
}
return k
})
for (let k in MULTI_KEYS) {
tmp[`${k}Key`] = tmp[`${k}Key`] || false
}
dict.push(tmp)
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) {
let k1 = keys[0]
if (keys.length === 1) {
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 {
if (this.__EVENTS__[k1] && this.__EVENTS__[k1].next) {
let k2 = keys[1]
let i = 0
let sub
for (sub of this.__EVENTS__[k1].next) {
if (sub.id === k2) {
break
}
i++
}
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]
}
]
} else {
if (this.__EVENTS__[first].fn) {
this.__EVENTS__[first].fn.push(callback)
} else {
this.__EVENTS__[first].fn = [callback]
}
}
}
}
finally(callback) {
if (typeof callback === 'function') {
this.__finally__ = callback
}
}
}

8
src/index.js Normal file
View File

@ -0,0 +1,8 @@
/**
*
* @author yutent<yutent.io@gmail.com>
* @date 2020/10/29 16:48:26
*/
export { KeyboardV1 } from './Keyboard.v1';
export { KeyboardV2 } from './Keyboard.v2';

112
src/key.dict.v2.js Normal file
View File

@ -0,0 +1,112 @@
// 允许组合出现的辅助键
export const MULTI_KEYS = {
ctrl: 1,
shift: 1,
alt: 1,
meta: 1
}
export const MULTI_KEY_CODES = [200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212]
// 按键对应的code值
export const KEY_DICT = {
'0': 48, // Digit{N}, Numpad{N}
'1': 49,
'2': 50,
'3': 51,
'4': 52,
'5': 53,
'6': 54,
'7': 55,
'8': 56,
'9': 57,
"'": 222,
'*': 106,
'+': 107,
',': 188,
'-': [109, 189],
'.': [110, 190],
'/': [111, 191],
';': 186,
'=': 187,
'[': 219,
'\\': 220,
']': 221,
'`': 192,
a: 65,
b: 66,
c: 67,
d: 68,
e: 69,
f: 70,
g: 71,
h: 72,
i: 73,
j: 74,
k: 75,
l: 76,
m: 77,
n: 78,
o: 79,
p: 80,
q: 81,
r: 82,
s: 83,
t: 84,
u: 85,
v: 86,
w: 87,
x: 88,
y: 89,
z: 90,
enter: 13, // Enter, NumpadEnter
space: 32,
meta: 91, // MetaLeft, MetaRight
tab: 9,
backspace: 8,
numlock: 12,
capslock: 20,
esc: 27,
menu: 93,
insert: 45,
delete: 46,
pagedown: 34,
pageup: 33,
home: 36,
end: 35,
up: 38,
down: 40,
left: 37,
right: 39,
f1: 112,
f2: 113,
f3: 114,
f4: 115,
f5: 116,
f6: 117,
f7: 118,
f8: 119,
f9: 120,
f10: 121,
f11: 122,
f12: 123,
f13: 124, // Print键
f14: 125, // Screen键
f15: 126, // Pause键
print: 124, // Print键
screen: 125, // Screen键
pause: 126, // Pause键
shift_left: 200, // ShiftLeft
shift_right: 201, // ShiftRight
ctrl_left: 202, // ControlLeft
ctrl_right: 203, // ControlRight
alt_left: 204, // AltLeft
alt_right: 205, // AltRight
option_left: 207, // OptionLeft
option_right: 208, // OptionRight
win_left: 209, // WinLeft
win_right: 210, // WinRight
cmd_left: 211, // CmdRight
cmd_right: 212, // CmdRight
}

11
src/utils/bind.js Normal file
View File

@ -0,0 +1,11 @@
function bind(el, fn) {
;(el || document).addEventListener('keydown', fn, false)
return fn
}
function unbind(el, fn) {
;(el || document).removeEventListener('keydown', fn, false)
}
export { bind, unbind };

23
src/utils/check.js Normal file
View File

@ -0,0 +1,23 @@
// 按键检测
function check(ev) {
let checked = false
if (typeof this.keys.key === 'object') {
checked = this.keys.key.includes(ev.keyCode)
} else {
checked = ev.keyCode === this.keys.key
}
if (checked) {
for (let k in this.keys) {
if (ev[k] !== this.keys[k]) {
checked = false
break
}
}
}
return checked
}
export { check };

10
src/utils/hide.js Normal file
View File

@ -0,0 +1,10 @@
function hide(target, name, value) {
Object.defineProperty(target, name, {
value,
writable: true,
enumerable: false,
configurable: true
})
}
export { hide };

4
src/utils/index.js Normal file
View File

@ -0,0 +1,4 @@
export { bind, unbind } from './bind';
export { hide } from './hide';
export { check } from './check';
export { parseAction } from './parseAction';

43
src/utils/parseAction.js Normal file
View File

@ -0,0 +1,43 @@
import {KEY_DICT, MULTI_KEYS} from "../key.dict.v1";
import {hide} from "./hide";
function parseAction(act) {
var keys = []
var dict = []
var passed = true // 语法检测通过
act.forEach(it => {
var tmp = {}
it = it.split('+').map(k => {
k = k.trim().toLowerCase()
if (MULTI_KEYS[k]) {
if (tmp[`${k}Key`]) {
passed = false
console.error('功能键,同组中不能重复。⎣%s⎤', it)
} else {
tmp[`${k}Key`] = true
}
} else {
if (tmp.key) {
passed = false
console.error('非功能键,同组不能出现多个。⎣%s⎤', it)
} else {
hide(tmp, 'key', KEY_DICT[k])
}
}
return k
})
for (let k in MULTI_KEYS) {
tmp[`${k}Key`] = tmp[`${k}Key`] || false
}
dict.push(tmp)
keys.push(it.join('+'))
})
return { keys, dict, passed }
}
export { parseAction };