feat: 虚拟键盘显示与操作

This commit is contained in:
unanmed 2024-01-18 16:20:50 +08:00
parent e77ca3e376
commit 899d1fa68e
5 changed files with 162 additions and 13 deletions

View File

@ -5,13 +5,13 @@ export interface EmitableEvent {
[event: string]: (...params: any) => any;
}
interface Listener<T extends (...params: any) => any> {
export interface Listener<T extends (...params: any) => any> {
fn: T;
once?: boolean;
immediate?: boolean;
}
interface ListenerOptions {
export interface ListenerOptions {
once: boolean;
immediate: boolean;
}
@ -22,12 +22,12 @@ type EmitFn<F extends (...params: any) => any> = (
) => any;
export class EventEmitter<T extends EmitableEvent = {}> {
private events: {
protected events: {
[P in keyof T]?: Listener<T[P]>[];
} = {};
private emitted: (keyof T)[] = [];
private emitter: {
protected emitter: {
[P in keyof T]?: EmitFn<T[P]>;
} = {};

View File

@ -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 { gameKey } from '../init/hotkey';
import { unwarpBinary } from './hotkey';
import { deleteWith } from '@/plugin/utils';
import { cloneDeep } from 'lodash-es';
import { shallowReactive } from 'vue';
export interface KeyboardEmits {
key: KeyCode;
@ -17,16 +22,30 @@ interface KeyboardItem {
y: number;
width: number;
height: number;
active?: boolean;
}
interface AssistManager {
symbol: symbol;
end(): void;
}
interface VirtualKeyEmit {
preventDefault(): void;
}
type VirtualKeyEmitFn = (
item: KeyboardItem,
assist: number,
index: number,
ev: VirtualKeyEmit
) => void;
interface VirtualKeyboardEvent extends EmitableEvent {
add: (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;
keys: KeyboardItem[] = [];
assist: number = 0;
fontSize: number = 18;
scope: symbol = Symbol();
private scopeStack: symbol[] = [];
private onEmitKey: Record<symbol, Listener<VirtualKeyEmitFn>[]> = {};
constructor(id: string) {
super();
@ -50,8 +74,10 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
* @param item
*/
add(item: KeyboardItem) {
this.keys.push(item);
this.emit('add', item);
const i = shallowReactive(item);
this.keys.push(i);
this.emit('add', i);
return this;
}
/**
@ -61,6 +87,7 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
remove(item: KeyboardItem) {
deleteWith(this.keys, item);
this.emit('remove', item);
return this;
}
/**
@ -71,14 +98,49 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
withAssist(assist: number): AssistManager {
const thisAssist = this.assist;
this.assist = assist;
const symbol = this.createScope();
return {
symbol,
end: () => {
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
@ -93,6 +155,7 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
});
this.keys.push(...toClone);
this.emit('extend', keyboard);
}
/**
@ -100,9 +163,14 @@ export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
* @param key
*/
emitKey(key: KeyboardItem, index: number) {
const ev = generateKeyboardEvent(key.key, this.assist);
gameKey.emitKey(key.key, this.assist, 'up', ev);
this.emit('emit', key, this.assist, index);
let prevent = false;
const preventDefault = () => (prevent = true);
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) {

View File

@ -1,2 +1,3 @@
import './fixed';
import './hotkey';
import './keyboard';

View 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
});

View File

@ -1,16 +1,35 @@
<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>
<script lang="ts" setup>
import { Keyboard } from '@/core/main/custom/keyboard';
import { KeyboardEmits } from '@/core/main/custom/keyboard';
import { ref } from 'vue';
import { KeyCodeUtils } from '@/plugin/keyCodes';
const props = defineProps<{
keyboard: Keyboard;
}>();
const fontSize = props.keyboard.fontSize;
const [width, height] = (() => {
const key = props.keyboard;
const mw = Math.max(...key.keys.map(v => v.x));
@ -28,5 +47,39 @@ const emits = defineEmits<{
.keyboard-container {
width: v-bind(width);
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>