Files
pihkaal-me/app/pages/index.vue
Pihkaal fefd1e171a
All checks were successful
Build and Push Docker Image / build (push) Successful in 2m7s
fix(nds): hide help button on smaller devices
2026-02-26 11:47:28 +01:00

248 lines
6.0 KiB
Vue

<script setup lang="ts">
import { useWindowSize } from "@vueuse/core";
import gsap from "gsap";
import { LazyLagModal } from "#components";
const { isReady } = useAssets();
const app = useAppStore();
const overlay = useOverlay();
const lagModal = overlay.create(LazyLagModal);
watch(
() => app.lagDetected,
(detected) => {
if (detected) lagModal.open();
},
);
const topScreen = useTemplateRef("topScreen");
const bottomScreen = useTemplateRef("bottomScreen");
const topScreenCanvas = computed(() => topScreen.value?.canvas ?? null);
const bottomScreenCanvas = computed(() => bottomScreen.value?.canvas ?? null);
const isTouchDevice = () =>
"ontouchstart" in window || navigator.maxTouchPoints > 0;
const windowSize = useWindowSize();
const isLargeEnough = computed(
() => windowSize.width.value / windowSize.height.value > 614 / 667,
);
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");
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 (isTouchDevice()) {
const landscape = window.matchMedia("(orientation: landscape)");
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();
}
}
});
</script>
<template>
<LoadingScreen v-if="!isReady || !app.userHasInteracted" />
<div v-else>
<TresCanvas
v-if="app.settings.renderingMode === '3d'"
window-size
clear-color="#181818"
>
<TresPerspectiveCamera :args="[45, 1, 0.001, 1000]" />
<TresAmbientLight />
<TresDirectionalLight />
<NDS3D
v-if="topScreenCanvas && bottomScreenCanvas"
:top-screen-canvas="topScreenCanvas"
:bottom-screen-canvas="bottomScreenCanvas"
/>
</TresCanvas>
<NDS2D
:style="{
visibility: app.settings.renderingMode === '3d' ? 'hidden' : 'visible',
}"
>
<template #topScreen>
<Screen ref="topScreen">
<IntroTopScreen v-if="!app.booted" />
<HomeTopScreen v-else-if="app.screen === 'home'" />
<ContactTopScreen v-else-if="app.screen === 'contact'" />
<ProjectsTopScreen v-else-if="app.screen === 'projects'" />
<SettingsTopScreen v-else-if="app.screen === 'settings'" />
<GalleryTopScreen v-else-if="app.screen === 'gallery'" />
<AchievementsTopScreen v-else-if="app.screen === 'achievements'" />
<CreditsTopScreen v-else-if="app.screen === 'credits'" />
<AchievementsNotification />
<CommonConfetti screen="top" />
</Screen>
</template>
<template #bottomScreen>
<Screen ref="bottomScreen">
<IntroBottomScreen v-if="!app.booted" />
<HomeBottomScreen v-else-if="app.screen === 'home'" />
<ContactBottomScreen v-else-if="app.screen === 'contact'" />
<ProjectsBottomScreen v-else-if="app.screen === 'projects'" />
<SettingsBottomScreen v-else-if="app.screen === 'settings'" />
<GalleryBottomScreen v-else-if="app.screen === 'gallery'" />
<AchievementsBottomScreen v-else-if="app.screen === 'achievements'" />
<CreditsBottomScreen v-else-if="app.screen === 'credits'" />
<CommonConfetti screen="bottom" />
</Screen>
</template>
</NDS2D>
<button
v-if="app.hintsAllowed"
ref="helpButton"
class="help-btn"
@click="app.hintsVisible ? hideHelpLabels() : showHelpLabels()"
>
?
</button>
<Controls />
</div>
</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;
}
</style>