diff --git a/app/components/Projects/BottomScreen/BottomScreen.vue b/app/components/Projects/BottomScreen/BottomScreen.vue
index b8ea5bb..d28f5cd 100644
--- a/app/components/Projects/BottomScreen/BottomScreen.vue
+++ b/app/components/Projects/BottomScreen/BottomScreen.vue
@@ -8,10 +8,20 @@ onMounted(async () => {
store.$reset();
await store.loadProjects();
});
+
+watch(
+ () => store.loading,
+ () => {
+ if (!store.loading) {
+ store.animateIntro();
+ }
+ },
+);
-
-
-
+
+
+
+
diff --git a/app/components/Projects/BottomScreen/Buttons.vue b/app/components/Projects/BottomScreen/Buttons.vue
index fc8399e..5bd5e5e 100644
--- a/app/components/Projects/BottomScreen/Buttons.vue
+++ b/app/components/Projects/BottomScreen/Buttons.vue
@@ -90,7 +90,7 @@ useScreenClick((x, y) => {
store.scrollProjects("right");
} else if (circleContains(BUTTONS.quit.position, [x, y], CLICK_RADIUS)) {
startButtonAnimation("quit");
- throw new Error("quit");
+ store.animateOutro();
} else if (
circleContains(BUTTONS.link.position, [x, y], CLICK_RADIUS) &&
project?.link
@@ -102,9 +102,7 @@ useScreenClick((x, y) => {
});
useRender((ctx) => {
- if (!currentAnimation) return;
-
- if (currentAnimation.showButton) {
+ if (currentAnimation?.showButton) {
const image = BUTTONS[currentAnimation.type].image;
ctx.drawImage(
image!,
@@ -113,7 +111,7 @@ useRender((ctx) => {
);
}
- if (currentAnimation.showSmallCircle) {
+ if (currentAnimation?.showSmallCircle) {
ctx.drawImage(
assets.projects.bottomScreen.circleSmall,
currentAnimation.position[0] - 28,
@@ -121,15 +119,17 @@ useRender((ctx) => {
);
}
- if (currentAnimation.showBigCircle) {
+ if (currentAnimation?.showBigCircle) {
ctx.drawImage(
assets.projects.bottomScreen.circleBig,
currentAnimation.position[0] - 44,
currentAnimation.position[1] - 44,
);
}
-});
+ ctx.fillStyle = `rgba(0, 0, 0, ${store.isIntro ? store.intro.fadeOpacity : store.isOutro ? store.outro.fadeOpacity : 0})`;
+ ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+});
useKeyDown((key) => {
if (currentAnimation) return;
switch (key) {
diff --git a/app/components/Projects/TopScreen/Project.vue b/app/components/Projects/TopScreen/Project.vue
index c6ce896..6140a1f 100644
--- a/app/components/Projects/TopScreen/Project.vue
+++ b/app/components/Projects/TopScreen/Project.vue
@@ -124,6 +124,9 @@ useRender((ctx) => {
"black",
"black",
);
+
+ ctx.fillStyle = `rgba(0, 0, 0, ${store.isIntro ? store.intro.fadeOpacity : store.isOutro ? store.outro.fadeOpacity : 0})`;
+ ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
});
defineOptions({
diff --git a/app/components/Projects/TopScreen/TopScreen.vue b/app/components/Projects/TopScreen/TopScreen.vue
index 9f95050..1fd4e4f 100644
--- a/app/components/Projects/TopScreen/TopScreen.vue
+++ b/app/components/Projects/TopScreen/TopScreen.vue
@@ -6,7 +6,8 @@ const store = useProjectsStore();
-
-
-
+
+
+
+
diff --git a/app/stores/projects.ts b/app/stores/projects.ts
index 2843842..442b84b 100644
--- a/app/stores/projects.ts
+++ b/app/stores/projects.ts
@@ -13,6 +13,17 @@ export const useProjectsStore = defineStore("projects", {
currentProject: 0,
loading: true,
offsetX: 0,
+
+ intro: {
+ fadeOpacity: 1,
+ },
+
+ outro: {
+ fadeOpacity: 0,
+ },
+
+ isIntro: true,
+ isOutro: false,
}),
actions: {
@@ -97,5 +108,44 @@ export const useProjectsStore = defineStore("projects", {
},
);
},
+
+ animateIntro() {
+ this.isIntro = true;
+
+ gsap.fromTo(
+ this.intro,
+ { fadeOpacity: 1 },
+ {
+ delay: 3,
+ fadeOpacity: 0,
+ duration: 0.35,
+ ease: "none",
+ onComplete: () => {
+ this.isIntro = false;
+ },
+ },
+ );
+ },
+
+ animateOutro() {
+ this.isOutro = true;
+
+ gsap.fromTo(
+ this.outro,
+ { fadeOpacity: 0 },
+ {
+ delay: 0.5,
+ fadeOpacity: 1,
+ duration: 0.35,
+ ease: "none",
+ onComplete: () => {
+ setTimeout(() => {
+ const router = useRouter();
+ router.push({ query: {} });
+ }, 3000);
+ },
+ },
+ );
+ },
},
});