feat(credits): add in both nds and repo

This commit is contained in:
2026-02-24 14:38:15 +01:00
parent e40dd255af
commit 7035525dfc
16 changed files with 400 additions and 11 deletions

156
app/stores/credits.ts Normal file
View File

@@ -0,0 +1,156 @@
import gsap from "gsap";
export const CREDITS = ["model3d", "css2d", "nintendo"] as const;
export type CreditId = (typeof CREDITS)[number];
export const useCreditsStore = defineStore("credits", {
state: () => ({
fadeToBlack: {
opacity: 0,
active: false,
isOutro: false,
},
intro: {
stage1Opacity: 0,
itemOffsets: {} as Record<number, number>,
},
outro: {
stage1Opacity: 1,
stage2Opacity: 1,
},
isIntro: true,
isOutro: false,
}),
actions: {
animateFadeToBlackIntro() {
this.fadeToBlack.active = true;
this.fadeToBlack.isOutro = false;
gsap
.timeline({
onComplete: () => {
const app = useAppStore();
app.navigateTo("credits");
},
})
.fromTo(
this.fadeToBlack,
{ opacity: 0 },
{
opacity: 1,
duration: 0.4,
ease: "none",
},
);
},
animateFadeToBlackOutro() {
this.fadeToBlack.active = true;
this.fadeToBlack.isOutro = true;
this.fadeToBlack.opacity = 1;
gsap
.timeline({
onComplete: () => {
this.fadeToBlack.active = false;
this.isOutro = false;
},
})
.fromTo(
this.fadeToBlack,
{ opacity: 1 },
{
opacity: 0,
duration: 0.4,
ease: "none",
},
);
},
animateIntro(itemCount: number) {
this.isIntro = true;
this.isOutro = false;
for (let i = 0; i < itemCount; i++) {
this.intro.itemOffsets[i] = -CREDITS_LINE_HEIGHT;
}
const tl = gsap.timeline({
onComplete: () => {
this.isIntro = false;
},
});
tl.fromTo(
this.intro,
{ stage1Opacity: 0 },
{
stage1Opacity: 1,
duration: 0.5,
ease: "none",
},
0.5,
);
for (let i = 0; i < itemCount; i++) {
tl.to(
this.intro.itemOffsets,
{
[i]: 0,
duration: 0.4,
ease: "power2.out",
},
0.75 + i * 0.05,
);
}
},
animateOutro() {
this.isIntro = false;
this.isOutro = true;
gsap
.timeline()
.fromTo(
this.outro,
{ stage1Opacity: 1 },
{
stage1Opacity: 0,
duration: 0.3,
},
)
.fromTo(
this.outro,
{ stage2Opacity: 1 },
{
stage2Opacity: 0,
duration: 0.3,
},
"-=0.15",
)
.call(() => {
const app = useAppStore();
app.navigateTo(app.previousScreen);
this.animateFadeToBlackOutro();
});
},
getItemOffset(index: number): number {
return this.intro.itemOffsets[index] ?? 0;
},
},
});
export const CREDITS_LINE_HEIGHT = 14;
export const CREDITS_ENTRY_GAP = 12;
export const CREDITS_HEADER_Y = 30;
export const CREDITS_LIST_START_Y = CREDITS_HEADER_Y + 35;
export const CREDITS_ENTRY_HEIGHT = CREDITS_LINE_HEIGHT * 3 + CREDITS_ENTRY_GAP;
export const CREDITS_TOP_SCREEN_COUNT = Math.floor(
(LOGICAL_HEIGHT - CREDITS_LIST_START_Y) / CREDITS_ENTRY_HEIGHT,
);
export const CREDITS_BOTTOM_START_Y = 30;

View File

@@ -4,9 +4,9 @@ export type HomeButton =
| "projects"
| "contact"
| "gallery"
| "theme"
| "settings"
| "achievements";
| "achievements"
| "credits";
export const useHomeStore = defineStore("home", {
state: () => ({
@@ -32,7 +32,10 @@ export const useHomeStore = defineStore("home", {
actions: {
reset() {
const app = useAppStore();
if (app.previousScreen === "achievements") {
if (
app.previousScreen === "achievements" ||
app.previousScreen === "credits"
) {
return;
}
@@ -105,6 +108,12 @@ export const useHomeStore = defineStore("home", {
return;
}
if (to === "credits") {
const creditsScreen = useCreditsStore();
creditsScreen.animateFadeToBlackIntro();
return;
}
this.isOutro = true;
this.outro.animateTop = to !== "settings";