mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-31 23:29:27 +08:00
feat: 虚拟键盘显示与操作
This commit is contained in:
parent
e77ca3e376
commit
899d1fa68e
@ -5,13 +5,13 @@ export interface EmitableEvent {
|
|||||||
[event: string]: (...params: any) => any;
|
[event: string]: (...params: any) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Listener<T extends (...params: any) => any> {
|
export interface Listener<T extends (...params: any) => any> {
|
||||||
fn: T;
|
fn: T;
|
||||||
once?: boolean;
|
once?: boolean;
|
||||||
immediate?: boolean;
|
immediate?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ListenerOptions {
|
export interface ListenerOptions {
|
||||||
once: boolean;
|
once: boolean;
|
||||||
immediate: boolean;
|
immediate: boolean;
|
||||||
}
|
}
|
||||||
@ -22,12 +22,12 @@ type EmitFn<F extends (...params: any) => any> = (
|
|||||||
) => any;
|
) => any;
|
||||||
|
|
||||||
export class EventEmitter<T extends EmitableEvent = {}> {
|
export class EventEmitter<T extends EmitableEvent = {}> {
|
||||||
private events: {
|
protected events: {
|
||||||
[P in keyof T]?: Listener<T[P]>[];
|
[P in keyof T]?: Listener<T[P]>[];
|
||||||
} = {};
|
} = {};
|
||||||
private emitted: (keyof T)[] = [];
|
private emitted: (keyof T)[] = [];
|
||||||
|
|
||||||
private emitter: {
|
protected emitter: {
|
||||||
[P in keyof T]?: EmitFn<T[P]>;
|
[P in keyof T]?: EmitFn<T[P]>;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
import { EmitableEvent, EventEmitter } from '@/core/common/eventEmitter';
|
import {
|
||||||
|
EmitableEvent,
|
||||||
|
EventEmitter,
|
||||||
|
Listener
|
||||||
|
} from '@/core/common/eventEmitter';
|
||||||
import { KeyCode } from '@/plugin/keyCodes';
|
import { KeyCode } from '@/plugin/keyCodes';
|
||||||
import { gameKey } from '../init/hotkey';
|
import { gameKey } from '../init/hotkey';
|
||||||
import { unwarpBinary } from './hotkey';
|
import { unwarpBinary } from './hotkey';
|
||||||
import { deleteWith } from '@/plugin/utils';
|
import { deleteWith } from '@/plugin/utils';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
import { shallowReactive } from 'vue';
|
||||||
|
|
||||||
export interface KeyboardEmits {
|
export interface KeyboardEmits {
|
||||||
key: KeyCode;
|
key: KeyCode;
|
||||||
@ -17,16 +22,30 @@ interface KeyboardItem {
|
|||||||
y: number;
|
y: number;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
|
active?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AssistManager {
|
interface AssistManager {
|
||||||
|
symbol: symbol;
|
||||||
end(): void;
|
end(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface VirtualKeyEmit {
|
||||||
|
preventDefault(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type VirtualKeyEmitFn = (
|
||||||
|
item: KeyboardItem,
|
||||||
|
assist: number,
|
||||||
|
index: number,
|
||||||
|
ev: VirtualKeyEmit
|
||||||
|
) => void;
|
||||||
|
|
||||||
interface VirtualKeyboardEvent extends EmitableEvent {
|
interface VirtualKeyboardEvent extends EmitableEvent {
|
||||||
add: (item: KeyboardItem) => void;
|
add: (item: KeyboardItem) => void;
|
||||||
remove: (item: KeyboardItem) => void;
|
remove: (item: KeyboardItem) => void;
|
||||||
emit: (item: KeyboardItem, assist: number, index: number) => void;
|
extend: (extended: Keyboard) => void;
|
||||||
|
emit: VirtualKeyEmitFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,6 +57,11 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
|
|||||||
id: string;
|
id: string;
|
||||||
keys: KeyboardItem[] = [];
|
keys: KeyboardItem[] = [];
|
||||||
assist: number = 0;
|
assist: number = 0;
|
||||||
|
fontSize: number = 18;
|
||||||
|
|
||||||
|
scope: symbol = Symbol();
|
||||||
|
private scopeStack: symbol[] = [];
|
||||||
|
private onEmitKey: Record<symbol, Listener<VirtualKeyEmitFn>[]> = {};
|
||||||
|
|
||||||
constructor(id: string) {
|
constructor(id: string) {
|
||||||
super();
|
super();
|
||||||
@ -50,8 +74,10 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
|
|||||||
* @param item 按键信息
|
* @param item 按键信息
|
||||||
*/
|
*/
|
||||||
add(item: KeyboardItem) {
|
add(item: KeyboardItem) {
|
||||||
this.keys.push(item);
|
const i = shallowReactive(item);
|
||||||
this.emit('add', item);
|
this.keys.push(i);
|
||||||
|
this.emit('add', i);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,6 +87,7 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
|
|||||||
remove(item: KeyboardItem) {
|
remove(item: KeyboardItem) {
|
||||||
deleteWith(this.keys, item);
|
deleteWith(this.keys, item);
|
||||||
this.emit('remove', item);
|
this.emit('remove', item);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,14 +98,49 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
|
|||||||
withAssist(assist: number): AssistManager {
|
withAssist(assist: number): AssistManager {
|
||||||
const thisAssist = this.assist;
|
const thisAssist = this.assist;
|
||||||
this.assist = assist;
|
this.assist = assist;
|
||||||
|
const symbol = this.createScope();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
symbol,
|
||||||
end: () => {
|
end: () => {
|
||||||
this.assist = thisAssist;
|
this.assist = thisAssist;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创造一个虚拟按键作用域,所有监听的事件与其他作用域不冲突
|
||||||
|
* @returns 作用域的唯一标识符
|
||||||
|
*/
|
||||||
|
createScope() {
|
||||||
|
const symbol = Symbol();
|
||||||
|
this.scope = symbol;
|
||||||
|
this.scopeStack.push(symbol);
|
||||||
|
const ev: Listener<VirtualKeyEmitFn>[] = [];
|
||||||
|
this.onEmitKey[symbol] = ev;
|
||||||
|
// @ts-ignore
|
||||||
|
this.events = ev;
|
||||||
|
return symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 释放一个作用域,同时删除其中的所有监听器
|
||||||
|
*/
|
||||||
|
disposeScope() {
|
||||||
|
if (this.scopeStack.length === 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot dispose virtual key scope since there's no scope to be disposed.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const now = this.scopeStack.pop()!;
|
||||||
|
delete this.onEmitKey[now];
|
||||||
|
const symbol = this.scopeStack.at(-1);
|
||||||
|
if (!symbol) return;
|
||||||
|
this.scope = symbol;
|
||||||
|
// @ts-ignore
|
||||||
|
this.events = this.onEmitKey[symbol];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 继承一个按键的按键信息
|
* 继承一个按键的按键信息
|
||||||
* @param keyboard 要被继承的按键
|
* @param keyboard 要被继承的按键
|
||||||
@ -93,6 +155,7 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.keys.push(...toClone);
|
this.keys.push(...toClone);
|
||||||
|
this.emit('extend', keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,9 +163,14 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
|
|||||||
* @param key 要触发的按键
|
* @param key 要触发的按键
|
||||||
*/
|
*/
|
||||||
emitKey(key: KeyboardItem, index: number) {
|
emitKey(key: KeyboardItem, index: number) {
|
||||||
const ev = generateKeyboardEvent(key.key, this.assist);
|
let prevent = false;
|
||||||
gameKey.emitKey(key.key, this.assist, 'up', ev);
|
const preventDefault = () => (prevent = true);
|
||||||
this.emit('emit', key, this.assist, index);
|
this.emit('emit', key, this.assist, index, { preventDefault });
|
||||||
|
|
||||||
|
if (!prevent) {
|
||||||
|
const ev = generateKeyboardEvent(key.key, this.assist);
|
||||||
|
gameKey.emitKey(key.key, this.assist, 'up', ev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get(id: string) {
|
static get(id: string) {
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
import './fixed';
|
import './fixed';
|
||||||
import './hotkey';
|
import './hotkey';
|
||||||
|
import './keyboard';
|
||||||
|
27
src/core/main/init/keyboard.ts
Normal file
27
src/core/main/init/keyboard.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { KeyCode } from '@/plugin/keyCodes';
|
||||||
|
import { Keyboard } from '../custom/keyboard';
|
||||||
|
|
||||||
|
const qweKey = new Keyboard('qwe');
|
||||||
|
qweKey.fontSize = 20;
|
||||||
|
qweKey
|
||||||
|
.add({
|
||||||
|
key: KeyCode.KeyQ,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 45,
|
||||||
|
height: 45
|
||||||
|
})
|
||||||
|
.add({
|
||||||
|
key: KeyCode.KeyW,
|
||||||
|
x: 50,
|
||||||
|
y: 0,
|
||||||
|
width: 45,
|
||||||
|
height: 45
|
||||||
|
})
|
||||||
|
.add({
|
||||||
|
key: KeyCode.KeyA,
|
||||||
|
x: 10,
|
||||||
|
y: 50,
|
||||||
|
width: 45,
|
||||||
|
height: 45
|
||||||
|
});
|
@ -1,16 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="keyboard-container"></div>
|
<div class="keyboard-container">
|
||||||
|
<div
|
||||||
|
v-for="(key, i) of keyboard.keys"
|
||||||
|
class="keyboard-item"
|
||||||
|
@click="keyboard.emitKey(key, i)"
|
||||||
|
:active="!!key.active"
|
||||||
|
:style="{
|
||||||
|
left: `${key.x}px`,
|
||||||
|
top: `${key.y}px`,
|
||||||
|
width: `${key.width}px`,
|
||||||
|
height: `${key.height}px`
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<span class="keyboard-key button-text">{{
|
||||||
|
key.text ?? KeyCodeUtils.toString(key.key)
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Keyboard } from '@/core/main/custom/keyboard';
|
import { Keyboard } from '@/core/main/custom/keyboard';
|
||||||
import { KeyboardEmits } from '@/core/main/custom/keyboard';
|
import { KeyboardEmits } from '@/core/main/custom/keyboard';
|
||||||
import { ref } from 'vue';
|
import { KeyCodeUtils } from '@/plugin/keyCodes';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
keyboard: Keyboard;
|
keyboard: Keyboard;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const fontSize = props.keyboard.fontSize;
|
||||||
|
|
||||||
const [width, height] = (() => {
|
const [width, height] = (() => {
|
||||||
const key = props.keyboard;
|
const key = props.keyboard;
|
||||||
const mw = Math.max(...key.keys.map(v => v.x));
|
const mw = Math.max(...key.keys.map(v => v.x));
|
||||||
@ -28,5 +47,39 @@ const emits = defineEmits<{
|
|||||||
.keyboard-container {
|
.keyboard-container {
|
||||||
width: v-bind(width);
|
width: v-bind(width);
|
||||||
height: v-bind(height);
|
height: v-bind(height);
|
||||||
|
display: block;
|
||||||
|
font-size: v-bind(fontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-item {
|
||||||
|
position: absolute;
|
||||||
|
background-color: #222;
|
||||||
|
border: 1.5px solid #ddd8;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
transition: all 0.1s linear;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-item:hover,
|
||||||
|
.keyboard-item[active='true'] {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-key[active='true'] {
|
||||||
|
color: gold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-key {
|
||||||
|
height: 100%;
|
||||||
|
min-width: 50px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
text-overflow: clip;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user