fix: hide help buttons on screen too small to click on physical buttons

This commit is contained in:
2026-10-07 13:58:03 +02:00
parent 06b96fe5ba
commit f1ee6922b1
4 changed files with 44 additions and 31 deletions

View File

@@ -95,11 +95,9 @@ const handleTouchEnd = (event: TouchEvent) => {
); );
}; };
const ndsScale = computed(() => { const ndsScale = computed(() =>
const scaleX = (windowSize.width.value - 40) / 235; getNDS2DScale(windowSize.width.value, windowSize.height.value),
const scaleY = (windowSize.height.value - 40) / 431; );
return Math.min(scaleX, scaleY);
});
const gallery = useGalleryStore(); const gallery = useGalleryStore();
const TOP_SCREEN_OFFSET = 170; const TOP_SCREEN_OFFSET = 170;

View File

@@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import { useWindowSize } from "@vueuse/core";
import gsap from "gsap"; import gsap from "gsap";
import { LazyLagModal } from "#components"; import { LazyLagModal } from "#components";
@@ -30,20 +29,6 @@ const bottomScreenCanvas = computed(() => bottomScreen.value?.canvas ?? null);
const isTouchDevice = () => const isTouchDevice = () =>
"ontouchstart" in window || navigator.maxTouchPoints > 0; "ontouchstart" in window || navigator.maxTouchPoints > 0;
const windowSize = useWindowSize();
watch(
[windowSize.width, windowSize.height],
([width, height]) => {
if (width / height > 614 / 667) {
if (app.booted) app.allowHints();
} else {
app.disallowHints();
}
},
{ immediate: true },
);
const helpButton = useTemplateRef("helpButton"); const helpButton = useTemplateRef("helpButton");
let helpAnimation: gsap.core.Timeline | null = null; let helpAnimation: gsap.core.Timeline | null = null;
@@ -105,11 +90,16 @@ const hideHelpLabels = () => {
}); });
}; };
// help button is removed when hints are disallowed, no need to animate it out
watch( watch(
() => app.hintsAllowed, () => app.hintsAllowed,
(allowed) => { (allowed) => {
if (!allowed) hideHelpLabels(); if (allowed) return;
helpAnimation?.kill();
helpAnimation = null;
app.hintsVisible = false;
}, },
{ immediate: true },
); );
onMounted(async () => { onMounted(async () => {
@@ -129,9 +119,7 @@ onMounted(async () => {
}); });
} }
const scaleX = (window.innerWidth - 40) / 235; const scale = getNDS2DScale(window.innerWidth, window.innerHeight);
const scaleY = (window.innerHeight - 40) / 431;
const scale = Math.min(scaleX, scaleY);
if (app.settings.renderingMode === "2d" && scale < 1) return; if (app.settings.renderingMode === "2d" && scale < 1) return;

View File

@@ -1,4 +1,5 @@
import { z } from "zod/mini"; import { z } from "zod/mini";
import { useWindowSize } from "@vueuse/core";
import type * as THREE from "three"; import type * as THREE from "three";
export const LOGICAL_WIDTH = 256; export const LOGICAL_WIDTH = 256;
@@ -18,6 +19,24 @@ export const APP_COLORS = [
const STORAGE_ID = "nds-settings"; const STORAGE_ID = "nds-settings";
// 2d nds is 414x431, scaled so the screens fit, it overflows horizontally on narrow viewports
export const getNDS2DScale = (width: number, height: number) =>
Math.min((width - 40) / 235, (height - 40) / 431);
// 3d: fixed vertical fov, so buttons clip at a fixed aspect ratio (measured: 810px wide at 865px tall)
const HINTS_MIN_ASPECT_RATIO_3D = 810 / 865;
// 2d: horizontal span of the physical buttons (A button is ~14px from the edge)
const NDS2D_BUTTONS_SPAN = 386;
const screenFitsHints = (
width: number,
height: number,
renderingMode: "3d" | "2d",
) =>
renderingMode === "3d"
? width / height > HINTS_MIN_ASPECT_RATIO_3D
: NDS2D_BUTTONS_SPAN * getNDS2DScale(width, height) <= width;
const settingsSchema = z.object({ const settingsSchema = z.object({
color: z.object({ color: z.object({
col: z.number(), col: z.number(),
@@ -55,7 +74,9 @@ export const useAppStore = defineStore("app", {
visitedGallery: false, visitedGallery: false,
camera: null as THREE.Camera | null, camera: null as THREE.Camera | null,
hintsVisible: false, hintsVisible: false,
hintsAllowed: false, // hints wanted by the current app state (intro done, not in gallery)
hintsRequested: false,
windowSize: useWindowSize(),
lagDetected: false, lagDetected: false,
lagModalOpen: false, lagModalOpen: false,
}; };
@@ -96,12 +117,11 @@ export const useAppStore = defineStore("app", {
}, },
allowHints() { allowHints() {
this.hintsAllowed = true; this.hintsRequested = true;
}, },
disallowHints() { disallowHints() {
this.hintsAllowed = false; this.hintsRequested = false;
this.hintsVisible = false;
}, },
showHints() { showHints() {
@@ -128,5 +148,14 @@ export const useAppStore = defineStore("app", {
row: state.settings.color.row, row: state.settings.color.row,
hex: APP_COLORS[state.settings.color.row]![state.settings.color.col]!, hex: APP_COLORS[state.settings.color.row]![state.settings.color.col]!,
}), }),
// hints wanted and screen large enough
hintsAllowed: (state) =>
state.hintsRequested &&
screenFitsHints(
state.windowSize.width,
state.windowSize.height,
state.settings.renderingMode,
),
}, },
}); });

View File

@@ -67,9 +67,7 @@ export const useIntroStore = defineStore("intro", {
) )
.call(() => { .call(() => {
const app = useAppStore(); const app = useAppStore();
if (window.innerWidth / window.innerHeight > 614 / 667) {
app.allowHints(); app.allowHints();
}
}); });
}, },