feat(2d-nds): intro animation
All checks were successful
Build and Push Docker Image / build (push) Successful in 2m47s

This commit is contained in:
2026-02-27 12:17:53 +01:00
parent 28df5ac25a
commit cd64082e84
3 changed files with 58 additions and 11 deletions

View File

@@ -6,6 +6,38 @@ const app = useAppStore();
const windowSize = useWindowSize();
const hintsContainer = useTemplateRef("hintsContainer");
const { assets } = useAssets();
const introState = reactive({
scaleMultiplier: 1,
rotateX: 0,
rotateY: 0,
opacity: 1,
});
let introPlayed = false;
onMounted(() => {
if (app.booted || app.settings.renderingMode !== "2d" || introPlayed) return;
introPlayed = true;
introState.scaleMultiplier = 0.6;
introState.rotateX = 70;
introState.rotateY = 0;
introState.opacity = 0;
assets.audio.whoosh.play();
gsap.to(introState, {
scaleMultiplier: 1,
rotateX: 0,
rotateY: 360,
opacity: 1,
duration: 2.8,
ease: "power2.inOut",
});
});
const buttonsDown = reactive(new Set<string>());
let mousePressedButton: string | null = null;
@@ -72,10 +104,19 @@ const ndsScale = computed(() => {
const gallery = useGalleryStore();
const TOP_SCREEN_OFFSET = 170;
const zoomStyle = computed(() => {
const scale = ndsScale.value * gallery.zoom.scale;
const ndsStyle = computed(() => {
const scale =
ndsScale.value * gallery.zoom.scale * introState.scaleMultiplier;
const y = TOP_SCREEN_OFFSET * ndsScale.value * (gallery.zoom.scale - 1);
return { transform: `translateY(${y}px) scale(${scale})` };
return {
transform: `translateY(${y}px) scale(${scale}) rotateX(${introState.rotateX}deg) rotateY(${introState.rotateY}deg)`,
opacity: introState.opacity,
};
});
const showBackFace = computed(() => {
const r = ((introState.rotateY % 360) + 360) % 360;
return r > 90 && r < 270;
});
watch(
@@ -102,8 +143,9 @@ watch(
<template>
<div class="nds2d-container">
<div class="nds2d" :style="zoomStyle">
<div class="nds2d-top-screen">
<div class="nds2d" :style="ndsStyle">
<div v-if="showBackFace" class="nds2d-top-screen"></div>
<div v-else class="nds2d-top-screen">
<div class="nds2d-speaker-hole nds2d-sh1"></div>
<div class="nds2d-speaker-hole nds2d-sh2"></div>
<div class="nds2d-screen nds2d-screen-top">
@@ -113,10 +155,11 @@ watch(
<div class="nds2d-speaker-hole nds2d-sh4"></div>
</div>
<div class="nds2d-hinge">
<div class="nds2d-mic"></div>
<div class="nds2d-light"></div>
<div v-if="!showBackFace" class="nds2d-mic"></div>
<div v-if="!showBackFace" class="nds2d-light"></div>
</div>
<div class="nds2d-bottom-screen">
<div v-if="showBackFace" class="nds2d-bottom-screen"></div>
<div v-else class="nds2d-bottom-screen">
<div class="nds2d-screen nds2d-screen-bottom">
<slot name="bottomScreen" />
</div>
@@ -246,6 +289,7 @@ watch(
text-align: center;
background: #181818;
overflow: hidden;
perspective: 800px;
}
.nds2d {
@@ -271,6 +315,7 @@ watch(
0px -1px 2px 2px #111;
}
.nds2d-screen {
position: absolute;
width: 51.5%;