feat(2d-nds): physical buttons
This commit is contained in:
@@ -6,6 +6,49 @@ const app = useAppStore();
|
|||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
const hintsContainer = useTemplateRef("hintsContainer");
|
const hintsContainer = useTemplateRef("hintsContainer");
|
||||||
|
|
||||||
|
const buttonsDown = reactive(new Set<string>());
|
||||||
|
let mousePressedButton: string | null = null;
|
||||||
|
|
||||||
|
const dPadStyle = computed(() => {
|
||||||
|
const rx =
|
||||||
|
(buttonsDown.has("UP") ? -1 : 0) + (buttonsDown.has("DOWN") ? 1 : 0);
|
||||||
|
const ry =
|
||||||
|
(buttonsDown.has("LEFT") ? -1 : 0) + (buttonsDown.has("RIGHT") ? 1 : 0);
|
||||||
|
if (!rx && !ry) return {};
|
||||||
|
return { transform: `rotateX(${rx * 12}deg) rotateY(${ry * 12}deg)` };
|
||||||
|
});
|
||||||
|
|
||||||
|
const pressButton = (button: string) => {
|
||||||
|
if (mousePressedButton) {
|
||||||
|
buttonsDown.delete(mousePressedButton);
|
||||||
|
window.dispatchEvent(
|
||||||
|
new KeyboardEvent("keyup", { key: `NDS_${mousePressedButton}` }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
buttonsDown.add(button);
|
||||||
|
mousePressedButton = button;
|
||||||
|
window.dispatchEvent(new KeyboardEvent("keydown", { key: `NDS_${button}` }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const releaseButton = () => {
|
||||||
|
if (!mousePressedButton) return;
|
||||||
|
buttonsDown.delete(mousePressedButton);
|
||||||
|
window.dispatchEvent(
|
||||||
|
new KeyboardEvent("keyup", { key: `NDS_${mousePressedButton}` }),
|
||||||
|
);
|
||||||
|
mousePressedButton = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
useKeyDown(({ ndsButton }) => {
|
||||||
|
if (ndsButton) buttonsDown.add(ndsButton);
|
||||||
|
});
|
||||||
|
|
||||||
|
useKeyUp(({ ndsButton }) => {
|
||||||
|
if (ndsButton) buttonsDown.delete(ndsButton);
|
||||||
|
});
|
||||||
|
|
||||||
|
useMouseUp(releaseButton);
|
||||||
|
|
||||||
const ndsScale = computed(() => {
|
const ndsScale = computed(() => {
|
||||||
const scaleX = (windowSize.width.value - 40) / 235;
|
const scaleX = (windowSize.width.value - 40) / 235;
|
||||||
const scaleY = (windowSize.height.value - 40) / 431;
|
const scaleY = (windowSize.height.value - 40) / 431;
|
||||||
@@ -59,25 +102,77 @@ defineExpose({ ndsScale });
|
|||||||
|
|
||||||
<div class="nds2d-d-pad-shadow"></div>
|
<div class="nds2d-d-pad-shadow"></div>
|
||||||
<div class="nds2d-d-pad">
|
<div class="nds2d-d-pad">
|
||||||
|
<div class="nds2d-d-pad-inner" :style="dPadStyle">
|
||||||
<div class="nds2d-d-pad-base"></div>
|
<div class="nds2d-d-pad-base"></div>
|
||||||
<div class="nds2d-d-pad-light"></div>
|
<div class="nds2d-d-pad-light"></div>
|
||||||
<div class="nds2d-d-pad-tip nds2d-tip-up"></div>
|
<div
|
||||||
<div class="nds2d-d-pad-tip nds2d-tip-down"></div>
|
class="nds2d-d-pad-tip nds2d-tip-up"
|
||||||
<div class="nds2d-d-pad-tip nds2d-tip-left"></div>
|
:class="{ pressed: buttonsDown.has('UP') }"
|
||||||
<div class="nds2d-d-pad-tip nds2d-tip-right"></div>
|
@mousedown.prevent="pressButton('UP')"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="nds2d-d-pad-tip nds2d-tip-down"
|
||||||
|
:class="{ pressed: buttonsDown.has('DOWN') }"
|
||||||
|
@mousedown.prevent="pressButton('DOWN')"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="nds2d-d-pad-tip nds2d-tip-left"
|
||||||
|
:class="{ pressed: buttonsDown.has('LEFT') }"
|
||||||
|
@mousedown.prevent="pressButton('LEFT')"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="nds2d-d-pad-tip nds2d-tip-right"
|
||||||
|
:class="{ pressed: buttonsDown.has('RIGHT') }"
|
||||||
|
@mousedown.prevent="pressButton('RIGHT')"
|
||||||
|
></div>
|
||||||
<div class="nds2d-d-pad-marker nds2d-dpm-up"></div>
|
<div class="nds2d-d-pad-marker nds2d-dpm-up"></div>
|
||||||
<div class="nds2d-d-pad-marker nds2d-dpm-down"></div>
|
<div class="nds2d-d-pad-marker nds2d-dpm-down"></div>
|
||||||
<div class="nds2d-d-pad-marker nds2d-dpm-left"></div>
|
<div class="nds2d-d-pad-marker nds2d-dpm-left"></div>
|
||||||
<div class="nds2d-d-pad-marker nds2d-dpm-right"></div>
|
<div class="nds2d-d-pad-marker nds2d-dpm-right"></div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="nds2d-button nds2d-btn-x">X</div>
|
<div
|
||||||
<div class="nds2d-button nds2d-btn-a">A</div>
|
class="nds2d-button nds2d-btn-x"
|
||||||
<div class="nds2d-button nds2d-btn-b">B</div>
|
:class="{ pressed: buttonsDown.has('X') }"
|
||||||
<div class="nds2d-button nds2d-btn-y">Y</div>
|
@mousedown.prevent="pressButton('X')"
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="nds2d-button nds2d-btn-a"
|
||||||
|
:class="{ pressed: buttonsDown.has('A') }"
|
||||||
|
@mousedown.prevent="pressButton('A')"
|
||||||
|
>
|
||||||
|
A
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="nds2d-button nds2d-btn-b"
|
||||||
|
:class="{ pressed: buttonsDown.has('B') }"
|
||||||
|
@mousedown.prevent="pressButton('B')"
|
||||||
|
>
|
||||||
|
B
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="nds2d-button nds2d-btn-y"
|
||||||
|
:class="{ pressed: buttonsDown.has('Y') }"
|
||||||
|
@mousedown.prevent="pressButton('Y')"
|
||||||
|
>
|
||||||
|
Y
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="nds2d-small-button nds2d-start"></div>
|
<div
|
||||||
<div class="nds2d-small-button nds2d-select"></div>
|
class="nds2d-small-button nds2d-start"
|
||||||
|
:class="{ pressed: buttonsDown.has('START') }"
|
||||||
|
@mousedown.prevent="pressButton('START')"
|
||||||
|
></div>
|
||||||
|
<div class="nds2d-small-button-label nds2d-start-label">START</div>
|
||||||
|
<div
|
||||||
|
class="nds2d-small-button nds2d-select"
|
||||||
|
:class="{ pressed: buttonsDown.has('SELECT') }"
|
||||||
|
@mousedown.prevent="pressButton('SELECT')"
|
||||||
|
></div>
|
||||||
|
<div class="nds2d-small-button-label nds2d-select-label">SELECT</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
ref="hintsContainer"
|
ref="hintsContainer"
|
||||||
@@ -335,6 +430,13 @@ defineExpose({ ndsScale });
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nds2d-d-pad-inner {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
transition: transform 0.06s ease;
|
||||||
|
}
|
||||||
|
|
||||||
.nds2d-d-pad-base {
|
.nds2d-d-pad-base {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -351,6 +453,10 @@ defineExpose({ ndsScale });
|
|||||||
|
|
||||||
.nds2d-d-pad-tip {
|
.nds2d-d-pad-tip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
transition:
|
||||||
|
filter 0.06s ease,
|
||||||
|
box-shadow 0.06s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nds2d-tip-up {
|
.nds2d-tip-up {
|
||||||
@@ -361,6 +467,11 @@ defineExpose({ ndsScale });
|
|||||||
box-shadow: inset 0 2px 3px -1px #444;
|
box-shadow: inset 0 2px 3px -1px #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nds2d-tip-up.pressed {
|
||||||
|
filter: brightness(0.75);
|
||||||
|
box-shadow: inset 0 6px 4px -6px #222;
|
||||||
|
}
|
||||||
|
|
||||||
.nds2d-tip-down {
|
.nds2d-tip-down {
|
||||||
left: 19px;
|
left: 19px;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@@ -369,6 +480,11 @@ defineExpose({ ndsScale });
|
|||||||
box-shadow: inset 0 -2px 3px -1px #444;
|
box-shadow: inset 0 -2px 3px -1px #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nds2d-tip-down.pressed {
|
||||||
|
filter: brightness(0.75);
|
||||||
|
box-shadow: inset 0 -6px 4px -6px #222;
|
||||||
|
}
|
||||||
|
|
||||||
.nds2d-tip-left {
|
.nds2d-tip-left {
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 19px;
|
top: 19px;
|
||||||
@@ -377,6 +493,11 @@ defineExpose({ ndsScale });
|
|||||||
box-shadow: inset 2px 0 3px -1px #444;
|
box-shadow: inset 2px 0 3px -1px #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nds2d-tip-left.pressed {
|
||||||
|
filter: brightness(0.75);
|
||||||
|
box-shadow: inset 6px 0 4px -6px #222;
|
||||||
|
}
|
||||||
|
|
||||||
.nds2d-tip-right {
|
.nds2d-tip-right {
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 19px;
|
top: 19px;
|
||||||
@@ -385,9 +506,15 @@ defineExpose({ ndsScale });
|
|||||||
box-shadow: inset -2px 0 3px -1px #444;
|
box-shadow: inset -2px 0 3px -1px #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nds2d-tip-right.pressed {
|
||||||
|
filter: brightness(0.75);
|
||||||
|
box-shadow: inset -6px 0 4px -6px #222;
|
||||||
|
}
|
||||||
|
|
||||||
.nds2d-d-pad-marker {
|
.nds2d-d-pad-marker {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: #999;
|
background: #999;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nds2d-dpm-up,
|
.nds2d-dpm-up,
|
||||||
@@ -424,6 +551,7 @@ defineExpose({ ndsScale });
|
|||||||
|
|
||||||
.nds2d-button {
|
.nds2d-button {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 21px;
|
width: 21px;
|
||||||
height: 21px;
|
height: 21px;
|
||||||
@@ -438,6 +566,16 @@ defineExpose({ ndsScale });
|
|||||||
box-shadow:
|
box-shadow:
|
||||||
3px 2px 3px -2px #111,
|
3px 2px 3px -2px #111,
|
||||||
inset 2px 2px 3px -1px #444;
|
inset 2px 2px 3px -1px #444;
|
||||||
|
transition:
|
||||||
|
box-shadow 0.06s ease,
|
||||||
|
transform 0.06s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds2d-button.pressed {
|
||||||
|
box-shadow:
|
||||||
|
1px 1px 1px -1px #111,
|
||||||
|
inset 2px 2px 3px -1px #222;
|
||||||
|
transform: scale(0.91) translateY(1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nds2d-btn-x {
|
.nds2d-btn-x {
|
||||||
@@ -462,6 +600,7 @@ defineExpose({ ndsScale });
|
|||||||
|
|
||||||
.nds2d-small-button {
|
.nds2d-small-button {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 11px;
|
width: 11px;
|
||||||
height: 11px;
|
height: 11px;
|
||||||
@@ -473,6 +612,16 @@ defineExpose({ ndsScale });
|
|||||||
box-shadow:
|
box-shadow:
|
||||||
3px 2px 3px -2px #111,
|
3px 2px 3px -2px #111,
|
||||||
inset 2px 2px 3px -1px #444;
|
inset 2px 2px 3px -1px #444;
|
||||||
|
transition:
|
||||||
|
box-shadow 0.06s ease,
|
||||||
|
transform 0.06s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nds2d-small-button.pressed {
|
||||||
|
box-shadow:
|
||||||
|
1px 1px 1px -1px #111,
|
||||||
|
inset 1px 1px 2px -1px #222;
|
||||||
|
transform: scale(0.88) translateY(1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nds2d-select {
|
.nds2d-select {
|
||||||
@@ -480,22 +629,21 @@ defineExpose({ ndsScale });
|
|||||||
right: 70px;
|
right: 70px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nds2d-start::after {
|
.nds2d-small-button-label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
content: "START";
|
|
||||||
font-size: 7px;
|
font-size: 7px;
|
||||||
color: #999;
|
color: #999;
|
||||||
left: 15px;
|
pointer-events: none;
|
||||||
top: 0px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nds2d-select::after {
|
.nds2d-start-label {
|
||||||
position: absolute;
|
bottom: 53px;
|
||||||
content: "SELECT";
|
right: 44px;
|
||||||
font-size: 7px;
|
}
|
||||||
color: #999;
|
|
||||||
left: 15px;
|
.nds2d-select-label {
|
||||||
top: 0px;
|
bottom: 29px;
|
||||||
|
right: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nds2d-hints-container {
|
.nds2d-hints-container {
|
||||||
|
|||||||
Reference in New Issue
Block a user