mirror of
https://github.com/AkiChase/scrcpy-mask
synced 2025-02-23 23:42:16 +08:00
feat(KeyBoard): add skill and observation button
This commit is contained in:
parent
3b4cb410dc
commit
a37896c2cb
@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Ref, onActivated, ref } from "vue";
|
import { onActivated } from "vue";
|
||||||
import { NDialog, useMessage } from "naive-ui";
|
import { NDialog, useMessage } from "naive-ui";
|
||||||
import { useGlobalStore } from "../store/global";
|
import { useGlobalStore } from "../store/global";
|
||||||
import { onBeforeRouteLeave, useRouter } from "vue-router";
|
import { onBeforeRouteLeave, useRouter } from "vue-router";
|
||||||
@ -10,14 +10,12 @@ import {
|
|||||||
unlistenToKeyEvent,
|
unlistenToKeyEvent,
|
||||||
updateScreenSizeAndMaskArea,
|
updateScreenSizeAndMaskArea,
|
||||||
} from "../hotkey";
|
} from "../hotkey";
|
||||||
import { getCurrent } from "@tauri-apps/api/window";
|
import { KeySteeringWheel } from "../keyMappingConfig";
|
||||||
|
|
||||||
const store = useGlobalStore();
|
const store = useGlobalStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
|
|
||||||
const renderedButtons: Ref<any[]> = ref([]);
|
|
||||||
|
|
||||||
onBeforeRouteLeave(() => {
|
onBeforeRouteLeave(() => {
|
||||||
const maskElement = document.getElementById("maskElement") as HTMLElement;
|
const maskElement = document.getElementById("maskElement") as HTMLElement;
|
||||||
|
|
||||||
@ -31,15 +29,9 @@ onActivated(async () => {
|
|||||||
const maskElement = document.getElementById("maskElement") as HTMLElement;
|
const maskElement = document.getElementById("maskElement") as HTMLElement;
|
||||||
|
|
||||||
if (store.controledDevice) {
|
if (store.controledDevice) {
|
||||||
const mt = 30;
|
|
||||||
const ml = 70;
|
|
||||||
const appWindow = getCurrent();
|
|
||||||
const size = (await appWindow.outerSize()).toLogical(
|
|
||||||
await appWindow.scaleFactor()
|
|
||||||
);
|
|
||||||
updateScreenSizeAndMaskArea(
|
updateScreenSizeAndMaskArea(
|
||||||
[store.screenSizeW, store.screenSizeH],
|
[store.screenSizeW, store.screenSizeH],
|
||||||
[size.width - ml, size.height - mt]
|
[maskElement.clientWidth, maskElement.clientHeight]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -48,7 +40,6 @@ onActivated(async () => {
|
|||||||
store.keyMappingConfigList[store.curKeyMappingIndex]
|
store.keyMappingConfigList[store.curKeyMappingIndex]
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
refreshKeyMappingButton();
|
|
||||||
listenToKeyEvent();
|
listenToKeyEvent();
|
||||||
} else {
|
} else {
|
||||||
message.error("按键方案异常,请删除此方案");
|
message.error("按键方案异常,请删除此方案");
|
||||||
@ -59,32 +50,6 @@ onActivated(async () => {
|
|||||||
function toStartServer() {
|
function toStartServer() {
|
||||||
router.replace({ name: "device" });
|
router.replace({ name: "device" });
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshKeyMappingButton() {
|
|
||||||
const maskElement = document.getElementById("maskElement") as HTMLElement;
|
|
||||||
|
|
||||||
const curKeyMappingConfig =
|
|
||||||
store.keyMappingConfigList[store.curKeyMappingIndex];
|
|
||||||
const relativeSize = curKeyMappingConfig.relativeSize;
|
|
||||||
const maskSizeW = maskElement.clientWidth;
|
|
||||||
const maskSizeH = maskElement.clientHeight;
|
|
||||||
const relativePosToMaskPos = (x: number, y: number) => {
|
|
||||||
return {
|
|
||||||
x: Math.round((x / relativeSize.w) * maskSizeW),
|
|
||||||
y: Math.round((y / relativeSize.h) * maskSizeH),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
const buttons = [];
|
|
||||||
for (let keyObject of curKeyMappingConfig.list) {
|
|
||||||
const { x, y } = relativePosToMaskPos(keyObject.posX, keyObject.posY);
|
|
||||||
buttons.push({
|
|
||||||
...keyObject,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
renderedButtons.value = buttons;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -101,29 +66,32 @@ function refreshKeyMappingButton() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-show="store.controledDevice"
|
v-if="store.keyMappingConfigList.length"
|
||||||
@contextmenu.prevent
|
@contextmenu.prevent
|
||||||
class="mask"
|
class="mask"
|
||||||
id="maskElement"
|
id="maskElement"
|
||||||
>
|
>
|
||||||
<template v-for="button in renderedButtons">
|
<template
|
||||||
|
v-for="button in store.keyMappingConfigList[store.curKeyMappingIndex]
|
||||||
|
.list"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-if="button.type === 'SteeringWheel'"
|
v-if="button.type === 'SteeringWheel'"
|
||||||
class="mask-steering-wheel"
|
class="mask-steering-wheel"
|
||||||
:style="{
|
:style="{
|
||||||
left: button.x - 75 + 'px',
|
left: button.posX - 75 + 'px',
|
||||||
top: button.y - 75 + 'px',
|
top: button.posY - 75 + 'px',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="wheel-container">
|
<div class="wheel-container">
|
||||||
<i />
|
<i />
|
||||||
<span>{{ button.key.up }}</span>
|
<span>{{ (button as KeySteeringWheel).key.up }}</span>
|
||||||
<i />
|
<i />
|
||||||
<span>{{ button.key.left }}</span>
|
<span>{{ (button as KeySteeringWheel).key.left }}</span>
|
||||||
<i />
|
<i />
|
||||||
<span>{{ button.key.right }}</span>
|
<span>{{ (button as KeySteeringWheel).key.right }}</span>
|
||||||
<i />
|
<i />
|
||||||
<span>{{ button.key.down }}</span>
|
<span>{{ (button as KeySteeringWheel).key.down }}</span>
|
||||||
<i />
|
<i />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -131,8 +99,8 @@ function refreshKeyMappingButton() {
|
|||||||
v-else
|
v-else
|
||||||
class="mask-button"
|
class="mask-button"
|
||||||
:style="{
|
:style="{
|
||||||
left: button.x + 'px',
|
left: button.posX + 'px',
|
||||||
top: button.y - 14 + 'px',
|
top: button.posY - 14 + 'px',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ button.key }}
|
{{ button.key }}
|
||||||
@ -183,6 +151,12 @@ function refreshKeyMappingButton() {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
z-index: 1;
|
||||||
|
position: absolute;
|
||||||
|
left: 70px;
|
||||||
|
top: 30px;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
|
@ -4,19 +4,22 @@ import KeyInfo from "./KeyInfo.vue";
|
|||||||
import KeySetting from "./KeySetting.vue";
|
import KeySetting from "./KeySetting.vue";
|
||||||
import KeyCommon from "./KeyCommon.vue";
|
import KeyCommon from "./KeyCommon.vue";
|
||||||
import KeySteeringWheel from "./KeySteeringWheel.vue";
|
import KeySteeringWheel from "./KeySteeringWheel.vue";
|
||||||
|
import KeySkill from "./KeySkill.vue";
|
||||||
import { KeySteeringWheel as KeyMappingSteeringWheel } from "../../keyMappingConfig";
|
import { KeySteeringWheel as KeyMappingSteeringWheel } from "../../keyMappingConfig";
|
||||||
import { useGlobalStore } from "../../store/global";
|
import { useGlobalStore } from "../../store/global";
|
||||||
import { useDialog, useMessage } from "naive-ui";
|
import { useDialog, useMessage } from "naive-ui";
|
||||||
import { onBeforeRouteLeave } from "vue-router";
|
import { onBeforeRouteLeave } from "vue-router";
|
||||||
|
import KeyObservation from "./KeyObservation.vue";
|
||||||
|
|
||||||
// TODO 普通按钮 KeyMacro,KeyCancelSkill,KeyTap
|
// TODO 普通按钮 KeyMacro,KeyCancelSkill,KeyTap
|
||||||
// 单个键,KeyMacro有输入框
|
// KeyMacro有输入框
|
||||||
// TODO 方向轮盘按钮 KeySteeringWheel
|
// TODO 方向轮盘按钮 KeySteeringWheel
|
||||||
// 有四个按钮+offset
|
// offset
|
||||||
// TODO 技能按钮 KeyDirectionalSkill,KeyDirectionlessSkill,KeyTriggerWhenPressedSkill(有区分directional)
|
// TODO 技能按钮 KeyDirectionalSkill,KeyDirectionlessSkill,KeyTriggerWhenPressedSkill(有区分directional)
|
||||||
// 单个键,靠两个flag来区分这四种情况,还有range或time要视情况
|
// 靠两个flag来区分这四种情况,还有range或time要视情况
|
||||||
// TODO 添加视野按钮 KeyObservation
|
// TODO 添加视野按钮 KeyObservation
|
||||||
// 单个键,有灵敏度scale
|
// 有灵敏度scale
|
||||||
|
|
||||||
// TODO 按钮齿轮图标可修改、删除
|
// TODO 按钮齿轮图标可修改、删除
|
||||||
// TODO 右键空白区域添加按键
|
// TODO 右键空白区域添加按键
|
||||||
// TODO 设置界面添加本地数据编辑器(类似utools)
|
// TODO 设置界面添加本地数据编辑器(类似utools)
|
||||||
@ -190,6 +193,7 @@ onBeforeRouteLeave(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
|
v-if="store.keyMappingConfigList.length"
|
||||||
id="keyboardElement"
|
id="keyboardElement"
|
||||||
class="keyboard"
|
class="keyboard"
|
||||||
@mousedown="handleClick"
|
@mousedown="handleClick"
|
||||||
@ -211,6 +215,22 @@ onBeforeRouteLeave(() => {
|
|||||||
activeSteeringWheelButtonKeyIndex
|
activeSteeringWheelButtonKeyIndex
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
<KeySkill
|
||||||
|
v-else-if="
|
||||||
|
store.editKeyMappingList[index].type === 'DirectionalSkill' ||
|
||||||
|
store.editKeyMappingList[index].type === 'DirectionlessSkill' ||
|
||||||
|
store.editKeyMappingList[index].type === 'TriggerWhenPressedSkill'
|
||||||
|
"
|
||||||
|
@edit="edited = true"
|
||||||
|
:index="index"
|
||||||
|
v-model:active-index="activeButtonIndex"
|
||||||
|
/>
|
||||||
|
<KeyObservation
|
||||||
|
v-else-if="store.editKeyMappingList[index].type === 'Observation'"
|
||||||
|
@edit="edited = true"
|
||||||
|
:index="index"
|
||||||
|
v-model:active-index="activeButtonIndex"
|
||||||
|
/>
|
||||||
<KeyCommon
|
<KeyCommon
|
||||||
v-else
|
v-else
|
||||||
@edit="edited = true"
|
@edit="edited = true"
|
||||||
@ -227,6 +247,8 @@ onBeforeRouteLeave(() => {
|
|||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
|
||||||
.keyboard-button {
|
.keyboard-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
98
src/components/keyboard/KeyObservation.vue
Normal file
98
src/components/keyboard/KeyObservation.vue
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useGlobalStore } from "../../store/global";
|
||||||
|
import { NIcon } from "naive-ui";
|
||||||
|
import { Eye } from "@vicons/ionicons5";
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
edit: [];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
index: number;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const activeIndex = defineModel("activeIndex", { required: true });
|
||||||
|
|
||||||
|
const store = useGlobalStore();
|
||||||
|
const elementRef = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
|
function dragHandler(downEvent: MouseEvent) {
|
||||||
|
activeIndex.value = props.index;
|
||||||
|
const oldX = store.editKeyMappingList[props.index].posX;
|
||||||
|
const oldY = store.editKeyMappingList[props.index].posY;
|
||||||
|
const element = elementRef.value;
|
||||||
|
if (element) {
|
||||||
|
const keyboardElement = document.getElementById(
|
||||||
|
"keyboardElement"
|
||||||
|
) as HTMLElement;
|
||||||
|
const maxX = keyboardElement.clientWidth - 60;
|
||||||
|
const maxY = keyboardElement.clientHeight - 60;
|
||||||
|
|
||||||
|
const x = downEvent.clientX;
|
||||||
|
const y = downEvent.clientY;
|
||||||
|
const moveHandler = (moveEvent: MouseEvent) => {
|
||||||
|
let newX = oldX + moveEvent.clientX - x;
|
||||||
|
let newY = oldY + moveEvent.clientY - y;
|
||||||
|
newX = Math.max(0, Math.min(newX, maxX));
|
||||||
|
newY = Math.max(0, Math.min(newY, maxY));
|
||||||
|
store.editKeyMappingList[props.index].posX = newX;
|
||||||
|
store.editKeyMappingList[props.index].posY = newY;
|
||||||
|
};
|
||||||
|
window.addEventListener("mousemove", moveHandler);
|
||||||
|
const upHandler = () => {
|
||||||
|
window.removeEventListener("mousemove", moveHandler);
|
||||||
|
window.removeEventListener("mouseup", upHandler);
|
||||||
|
if (
|
||||||
|
oldX !== store.editKeyMappingList[props.index].posX ||
|
||||||
|
oldY !== store.editKeyMappingList[props.index].posY
|
||||||
|
) {
|
||||||
|
emit("edit");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener("mouseup", upHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="{ active: props.index === activeIndex }"
|
||||||
|
:style="{
|
||||||
|
left: `${store.editKeyMappingList[props.index].posX - 30}px`,
|
||||||
|
top: `${store.editKeyMappingList[props.index].posY - 30}px`,
|
||||||
|
}"
|
||||||
|
@mousedown="dragHandler"
|
||||||
|
class="key-observation"
|
||||||
|
ref="elementRef"
|
||||||
|
>
|
||||||
|
<NIcon size="25"><Eye style="color: var(--blue-color)" /></NIcon>
|
||||||
|
<span>{{ store.editKeyMappingList[props.index].key }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.key-observation {
|
||||||
|
position: absolute;
|
||||||
|
height: 60px;
|
||||||
|
width: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid var(--blue-color);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:not(.active):hover {
|
||||||
|
border: 2px solid var(--light-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.active {
|
||||||
|
border: 2px solid var(--primary-color);
|
||||||
|
color: var(--primary-color);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
</style>
|
97
src/components/keyboard/KeySkill.vue
Normal file
97
src/components/keyboard/KeySkill.vue
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useGlobalStore } from "../../store/global";
|
||||||
|
import { Flash } from "@vicons/ionicons5";
|
||||||
|
import { NIcon } from "naive-ui";
|
||||||
|
const emit = defineEmits<{
|
||||||
|
edit: [];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
index: number;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const activeIndex = defineModel("activeIndex", { required: true });
|
||||||
|
|
||||||
|
const store = useGlobalStore();
|
||||||
|
const elementRef = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
|
function dragHandler(downEvent: MouseEvent) {
|
||||||
|
activeIndex.value = props.index;
|
||||||
|
const oldX = store.editKeyMappingList[props.index].posX;
|
||||||
|
const oldY = store.editKeyMappingList[props.index].posY;
|
||||||
|
const element = elementRef.value;
|
||||||
|
if (element) {
|
||||||
|
const keyboardElement = document.getElementById(
|
||||||
|
"keyboardElement"
|
||||||
|
) as HTMLElement;
|
||||||
|
const maxX = keyboardElement.clientWidth - 60;
|
||||||
|
const maxY = keyboardElement.clientHeight - 60;
|
||||||
|
|
||||||
|
const x = downEvent.clientX;
|
||||||
|
const y = downEvent.clientY;
|
||||||
|
const moveHandler = (moveEvent: MouseEvent) => {
|
||||||
|
let newX = oldX + moveEvent.clientX - x;
|
||||||
|
let newY = oldY + moveEvent.clientY - y;
|
||||||
|
newX = Math.max(0, Math.min(newX, maxX));
|
||||||
|
newY = Math.max(0, Math.min(newY, maxY));
|
||||||
|
store.editKeyMappingList[props.index].posX = newX;
|
||||||
|
store.editKeyMappingList[props.index].posY = newY;
|
||||||
|
};
|
||||||
|
window.addEventListener("mousemove", moveHandler);
|
||||||
|
const upHandler = () => {
|
||||||
|
window.removeEventListener("mousemove", moveHandler);
|
||||||
|
window.removeEventListener("mouseup", upHandler);
|
||||||
|
if (
|
||||||
|
oldX !== store.editKeyMappingList[props.index].posX ||
|
||||||
|
oldY !== store.editKeyMappingList[props.index].posY
|
||||||
|
) {
|
||||||
|
emit("edit");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener("mouseup", upHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="{ active: props.index === activeIndex }"
|
||||||
|
:style="{
|
||||||
|
left: `${store.editKeyMappingList[props.index].posX - 30}px`,
|
||||||
|
top: `${store.editKeyMappingList[props.index].posY - 30}px`,
|
||||||
|
}"
|
||||||
|
@mousedown="dragHandler"
|
||||||
|
class="key-skill"
|
||||||
|
ref="elementRef"
|
||||||
|
>
|
||||||
|
<NIcon size="25"><Flash style="color: var(--blue-color)" /></NIcon>
|
||||||
|
<span>{{ store.editKeyMappingList[props.index].key }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.key-skill {
|
||||||
|
position: absolute;
|
||||||
|
height: 60px;
|
||||||
|
width: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid var(--blue-color);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:not(.active):hover {
|
||||||
|
border: 2px solid var(--light-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.active {
|
||||||
|
border: 2px solid var(--primary-color);
|
||||||
|
color: var(--primary-color);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user