feat(achievements): implement achievements screen

This commit is contained in:
2026-01-20 19:18:12 +01:00
parent 29e26f7f4e
commit 8e5a38a5c1
13 changed files with 394 additions and 12 deletions

View File

@@ -0,0 +1,124 @@
import gsap from "gsap";
export const useAchievementsScreen = defineStore("achievementsScreen", {
state: () => ({
intro: {
stage1Opacity: 0,
stage2Opacity: 0,
itemOffsets: {} as Record<number, number>,
progressBar: 0,
},
outro: {
stage1Opacity: 1,
stage2Opacity: 1,
stage3Opacity: 1,
},
isIntro: true,
isOutro: false,
}),
actions: {
animateIntro() {
this.isIntro = true;
this.isOutro = false;
const itemCount = ACHIEVEMENTS.length;
for (let i = 0; i < itemCount; i++) {
this.intro.itemOffsets[i] = -ACHIEVEMENTS_LINE_HEIGHT;
}
const tl = gsap.timeline({
onComplete: () => {
this.isIntro = false;
},
});
tl.fromTo(
this.intro,
{ stage1Opacity: 0 },
{
stage1Opacity: 1,
duration: 0.3,
ease: "none",
},
)
.fromTo(
this.intro,
{ stage2Opacity: 0 },
{
stage2Opacity: 1,
duration: 0.5,
ease: "none",
},
0.5,
)
.fromTo(
this.intro,
{ progressBar: 0 },
{
progressBar: 1,
duration: 1.5,
ease: "power2.out",
},
0.25,
);
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,
{ stage2Opacity: 1 },
{
stage2Opacity: 0,
duration: 0.3,
},
)
.fromTo(
this.outro,
{ stage3Opacity: 1 },
{
stage3Opacity: 0,
duration: 0.3,
},
"-=0.15",
)
.fromTo(
this.outro,
{ stage1Opacity: 1 },
{
stage1Opacity: 0,
duration: 0.3,
ease: "none",
},
)
.call(() => {
const app = useAppStore();
app.navigateTo("home");
});
},
getItemOffset(index: number): number {
return this.intro.itemOffsets[index] ?? 0;
},
},
});