feat(nds): add help button with hints for all physical buttons

This commit is contained in:
2026-02-22 21:16:13 +01:00
parent cdd6be0719
commit 48688544f1
6 changed files with 377 additions and 161 deletions

View File

@@ -1,14 +1,12 @@
<script setup lang="ts">
import type { Screen as NDSScreen } from "#components";
type ScreenInstance = InstanceType<typeof NDSScreen>;
import gsap from "gsap";
const { isReady } = useAssets();
const app = useAppStore();
const topScreen = useTemplateRef<ScreenInstance>("topScreen");
const bottomScreen = useTemplateRef<ScreenInstance>("bottomScreen");
const topScreen = useTemplateRef("topScreen");
const bottomScreen = useTemplateRef("bottomScreen");
const topScreenCanvas = computed(() => topScreen.value?.canvas ?? null);
const bottomScreenCanvas = computed(() => bottomScreen.value?.canvas ?? null);
@@ -27,27 +25,120 @@ const toggleFullscreen = () => {
}
};
onMounted(() => {
const helpButton = useTemplateRef("helpButton");
let helpAnimation: gsap.core.Timeline | null = null;
const showHelpLabels = async (yoyo = false) => {
if (!app.hintsAllowed) return;
helpAnimation?.kill();
app.hintsVisible = true;
await nextTick();
helpAnimation = gsap
.timeline({
onComplete: () => {
helpAnimation = null;
},
})
.fromTo(
helpButton.value,
{ color: "#666666", opacity: 0.5 },
{ color: "#ffffff", opacity: 1, duration: 0.2, ease: "power1.out" },
)
.to(helpButton.value, {
color: "#666666",
opacity: 0.5,
duration: 0.2,
ease: "power1.in",
delay: 3,
})
.call(() => {
app.hintsVisible = false;
});
if (yoyo) {
helpAnimation.to(helpButton.value, {
color: "#ffffff",
opacity: 1,
duration: 0.3,
repeat: 5,
yoyo: true,
delay: 0.3,
});
}
};
const hideHelpLabels = () => {
if (!app.hintsVisible) return;
helpAnimation?.kill();
helpAnimation = null;
app.hintsVisible = false;
gsap.to(helpButton.value, {
color: "#666666",
opacity: 0.5,
duration: 0.2,
ease: "power1.in",
});
};
watch(
() => app.hintsAllowed,
(allowed) => {
if (!allowed) hideHelpLabels();
},
);
onMounted(async () => {
if (isIOS()) {
showFullscreenBtn.value = false;
return;
}
if (!isTouchDevice()) return;
if (isTouchDevice()) {
const landscape = window.matchMedia("(orientation: landscape)");
const landscape = window.matchMedia("(orientation: landscape)");
const onOrientationChange = (e: MediaQueryListEvent | MediaQueryList) => {
if (e.matches && !document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(() => {});
}
};
const onOrientationChange = (e: MediaQueryListEvent | MediaQueryList) => {
if (e.matches && !document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(() => {});
landscape.addEventListener("change", onOrientationChange);
onUnmounted(() => {
landscape.removeEventListener("change", onOrientationChange);
});
}
const scaleX = (window.innerWidth - 40) / 235;
const scaleY = (window.innerHeight - 40) / 431;
const scale = Math.min(scaleX, scaleY);
if (app.settings.renderingMode === "2d" && scale < 1) return;
if (!app.booted) {
watch(
() => app.hintsAllowed,
async (allowed) => {
if (allowed) await showHelpLabels(true);
},
{ once: true },
);
}
});
useKeyDown(async ({ key, repeated }) => {
if (!repeated && key.toLocaleLowerCase() === "h") {
if (app.hintsVisible) {
hideHelpLabels();
} else {
await showHelpLabels();
}
};
landscape.addEventListener("change", onOrientationChange);
onUnmounted(() => {
landscape.removeEventListener("change", onOrientationChange);
});
}
});
</script>
@@ -105,6 +196,15 @@ onMounted(() => {
</template>
</NDS2D>
<button
v-if="app.hintsAllowed"
ref="helpButton"
class="help-btn"
@click="app.hintsVisible ? hideHelpLabels() : showHelpLabels()"
>
?
</button>
<button
v-if="showFullscreenBtn"
class="fullscreen-btn"
@@ -125,6 +225,28 @@ onMounted(() => {
</template>
<style scoped>
.help-btn {
position: fixed;
bottom: 16px;
left: 16px;
width: 30px;
height: 30px;
border: none;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
color: #666;
font-size: 18px;
cursor: pointer;
opacity: 0.5;
transition: opacity 0.2s;
user-select: none;
z-index: 100;
}
.help-btn:hover {
opacity: 1;
}
.fullscreen-btn {
position: fixed;
bottom: 16px;