feat(contact): animate outro
This commit is contained in:
@@ -7,10 +7,10 @@ const contactBackgroundImage = useTemplateRef("contactBackgroundImage");
|
|||||||
useRender((ctx) => {
|
useRender((ctx) => {
|
||||||
if (!homeBackgroundImage.value || !contactBackgroundImage.value) return;
|
if (!homeBackgroundImage.value || !contactBackgroundImage.value) return;
|
||||||
|
|
||||||
if (store.isIntro) {
|
ctx.drawImage(homeBackgroundImage.value, 0, 0);
|
||||||
ctx.drawImage(homeBackgroundImage.value, 0, 0);
|
ctx.globalAlpha = store.isIntro
|
||||||
ctx.globalAlpha = store.intro.stage2Opacity;
|
? store.intro.stage2Opacity
|
||||||
}
|
: store.outro.stage3Opacity;
|
||||||
|
|
||||||
ctx.drawImage(contactBackgroundImage.value, 0, 0);
|
ctx.drawImage(contactBackgroundImage.value, 0, 0);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ useRender((ctx) => {
|
|||||||
if (!topBarImage.value || !bottomBarImage.value || !bottomBarOkImage.value)
|
if (!topBarImage.value || !bottomBarImage.value || !bottomBarOkImage.value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ctx.globalAlpha = store.isIntro ? store.intro.stage3Opacity : 1;
|
ctx.globalAlpha = store.isIntro
|
||||||
|
? store.intro.stage3Opacity
|
||||||
|
: store.outro.stage2Opacity;
|
||||||
|
|
||||||
// top bar
|
// top bar
|
||||||
ctx.drawImage(topBarImage.value, 0, store.isIntro ? store.intro.topBarY : 0);
|
ctx.drawImage(topBarImage.value, 0, store.isIntro ? store.intro.topBarY : 0);
|
||||||
|
|||||||
@@ -41,20 +41,35 @@ const { selectedButton, selectorPosition } = useButtonNavigation({
|
|||||||
},
|
},
|
||||||
initialButton: "github",
|
initialButton: "github",
|
||||||
onButtonClick: async (button) => {
|
onButtonClick: async (button) => {
|
||||||
const [action, verb, content] = ACTIONS[button];
|
actionateButton(button);
|
||||||
if (action === "Copy") {
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(content);
|
|
||||||
store.pushNotification(`${verb} copied to clipboard`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to copy to clipboard:", error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await navigateTo(content, { open: { target: "_blank " } });
|
|
||||||
store.pushNotification(`${verb} opened`);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const actionateButton = async (button: (typeof selectedButton)["value"]) => {
|
||||||
|
const [action, verb, content] = ACTIONS[button];
|
||||||
|
if (action === "Copy") {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(content);
|
||||||
|
store.pushNotification(`${verb} copied to clipboard`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to copy to clipboard:", error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await navigateTo(content, { open: { target: "_blank " } });
|
||||||
|
store.pushNotification(`${verb} opened`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const QUIT_BUTTON: Rect = [31, 172, 80, 18];
|
||||||
|
const OK_BUTTON: Rect = [144, 172, 80, 18];
|
||||||
|
|
||||||
|
useScreenClick((x, y) => {
|
||||||
|
if (rectContains(QUIT_BUTTON, [x, y])) {
|
||||||
|
store.animateOutro();
|
||||||
|
} else if (rectContains(OK_BUTTON, [x, y])) {
|
||||||
|
actionateButton(selectedButton.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -63,7 +78,9 @@ const { selectedButton, selectorPosition } = useButtonNavigation({
|
|||||||
<Buttons />
|
<Buttons />
|
||||||
<ButtonSelector
|
<ButtonSelector
|
||||||
:rect="selectorPosition"
|
:rect="selectorPosition"
|
||||||
:opacity="store.isIntro ? store.intro.stage3Opacity : 1"
|
:opacity="
|
||||||
|
store.isIntro ? store.intro.stage3Opacity : store.outro.stage1Opacity
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Bars :ok-label="ACTIONS[selectedButton][0]" />
|
<Bars :ok-label="ACTIONS[selectedButton][0]" />
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ const buttonsImage = useTemplateRef("buttonsImage");
|
|||||||
useRender((ctx) => {
|
useRender((ctx) => {
|
||||||
if (!buttonsImage.value) return;
|
if (!buttonsImage.value) return;
|
||||||
|
|
||||||
ctx.globalAlpha = store.isIntro ? store.intro.stage3Opacity : 1;
|
ctx.globalAlpha = store.isIntro
|
||||||
|
? store.intro.stage3Opacity
|
||||||
|
: store.outro.stage1Opacity;
|
||||||
ctx.drawImage(buttonsImage.value, 31, 32);
|
ctx.drawImage(buttonsImage.value, 31, 32);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -7,12 +7,10 @@ const contactBackgroundImage = useTemplateRef("contactBackgroundImage");
|
|||||||
useRender((ctx) => {
|
useRender((ctx) => {
|
||||||
if (!homeBackgroundImage.value || !contactBackgroundImage.value) return;
|
if (!homeBackgroundImage.value || !contactBackgroundImage.value) return;
|
||||||
|
|
||||||
ctx.drawImage(contactBackgroundImage.value, 0, 0);
|
ctx.drawImage(homeBackgroundImage.value, 0, 0);
|
||||||
|
ctx.globalAlpha = store.isIntro
|
||||||
if (store.isIntro) {
|
? store.intro.stage2Opacity
|
||||||
ctx.drawImage(homeBackgroundImage.value, 0, 0);
|
: store.outro.stage3Opacity;
|
||||||
ctx.globalAlpha = store.intro.stage2Opacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.drawImage(contactBackgroundImage.value, 0, 0);
|
ctx.drawImage(contactBackgroundImage.value, 0, 0);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,10 +7,14 @@ const thingsImage = useTemplateRef("thingsImage");
|
|||||||
useRender((ctx) => {
|
useRender((ctx) => {
|
||||||
if (!backgroundImage.value || !thingsImage.value) return;
|
if (!backgroundImage.value || !thingsImage.value) return;
|
||||||
|
|
||||||
ctx.globalAlpha = store.isIntro ? store.intro.stage1Opacity : 1;
|
ctx.globalAlpha = store.isIntro
|
||||||
|
? store.intro.stage1Opacity
|
||||||
|
: store.outro.stage2Opacity;
|
||||||
ctx.drawImage(backgroundImage.value, 0, 0);
|
ctx.drawImage(backgroundImage.value, 0, 0);
|
||||||
|
|
||||||
ctx.globalAlpha = store.isIntro ? store.intro.stage3Opacity : 1;
|
ctx.globalAlpha = store.isIntro
|
||||||
|
? store.intro.stage3Opacity
|
||||||
|
: store.outro.stage1Opacity;
|
||||||
ctx.drawImage(thingsImage.value, 0, 0);
|
ctx.drawImage(thingsImage.value, 0, 0);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const titleImage = useTemplateRef("titleImage");
|
|||||||
useRender((ctx) => {
|
useRender((ctx) => {
|
||||||
if (!notificationImage.value || !titleImage.value) return;
|
if (!notificationImage.value || !titleImage.value) return;
|
||||||
|
|
||||||
|
ctx.globalAlpha = store.outro.stage2Opacity;
|
||||||
ctx.font = "10px NDS10";
|
ctx.font = "10px NDS10";
|
||||||
|
|
||||||
// notifications
|
// notifications
|
||||||
@@ -24,7 +25,9 @@ useRender((ctx) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// title
|
// title
|
||||||
ctx.globalAlpha = store.isIntro ? store.intro.stage1Opacity : 1;
|
ctx.globalAlpha = store.isIntro
|
||||||
|
? store.intro.stage1Opacity
|
||||||
|
: store.outro.stage2Opacity;
|
||||||
ctx.drawImage(
|
ctx.drawImage(
|
||||||
titleImage.value,
|
titleImage.value,
|
||||||
21,
|
21,
|
||||||
|
|||||||
@@ -13,7 +13,14 @@ export const useContactStore = defineStore("contact", {
|
|||||||
bottomBarY: SCREEN_HEIGHT + 20,
|
bottomBarY: SCREEN_HEIGHT + 20,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
outro: {
|
||||||
|
stage1Opacity: 1,
|
||||||
|
stage2Opacity: 1,
|
||||||
|
stage3Opacity: 1,
|
||||||
|
},
|
||||||
|
|
||||||
isIntro: true,
|
isIntro: true,
|
||||||
|
isOutro: true,
|
||||||
|
|
||||||
notifications: [] as string[],
|
notifications: [] as string[],
|
||||||
notificationsYOffset: 0,
|
notificationsYOffset: 0,
|
||||||
@@ -23,7 +30,7 @@ export const useContactStore = defineStore("contact", {
|
|||||||
animateIntro() {
|
animateIntro() {
|
||||||
this.isIntro = true;
|
this.isIntro = true;
|
||||||
|
|
||||||
const start = 3;
|
const start = 2;
|
||||||
|
|
||||||
gsap.fromTo(
|
gsap.fromTo(
|
||||||
this.intro,
|
this.intro,
|
||||||
@@ -83,5 +90,53 @@ export const useContactStore = defineStore("contact", {
|
|||||||
{ notificationsYOffset: 0, duration: 0.075 },
|
{ notificationsYOffset: 0, duration: 0.075 },
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
animateOutro() {
|
||||||
|
this.isOutro = true;
|
||||||
|
|
||||||
|
gsap.fromTo(
|
||||||
|
this.outro,
|
||||||
|
{
|
||||||
|
stage1Opacity: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stage1Opacity: 0,
|
||||||
|
duration: 0.2,
|
||||||
|
ease: "none",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
gsap.fromTo(
|
||||||
|
this.outro,
|
||||||
|
{
|
||||||
|
stage2Opacity: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stage2Opacity: 0,
|
||||||
|
duration: 0.25,
|
||||||
|
delay: 0.25,
|
||||||
|
ease: "none",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
gsap.fromTo(
|
||||||
|
this.outro,
|
||||||
|
{
|
||||||
|
stage3Opacity: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stage3Opacity: 0,
|
||||||
|
duration: 0.3,
|
||||||
|
delay: 0.5,
|
||||||
|
ease: "none",
|
||||||
|
onComplete: () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.isOutro = false;
|
||||||
|
navigateTo("/");
|
||||||
|
}, 2000);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
8
app/utils/math.ts
Normal file
8
app/utils/math.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export type Point = [x: number, y: number];
|
||||||
|
export type Rect = [x: number, y: number, width: number, height: number];
|
||||||
|
|
||||||
|
export function rectContains(rect: Rect, point: Point): boolean {
|
||||||
|
const [x, y, width, height] = rect;
|
||||||
|
const [px, py] = point;
|
||||||
|
return px >= x && px <= x + width && py >= y && py <= y + height;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user