feat(contact): intro animation

This commit is contained in:
2025-11-17 00:33:37 +01:00
parent 2efdb84234
commit 6383dc0697
18 changed files with 240 additions and 14 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -5,15 +5,17 @@ const props = withDefaults(
y: number;
width: number;
height: number;
animationSpeed?: number;
opacity?: number;
}>(),
{
animationSpeed: 0.25,
opacity: 1,
},
);
const cornerImage = useTemplateRef("cornerImage");
const ANIMATION_SPEED = 0.25;
let currentX = props.x;
let currentY = props.y;
let currentWidth = props.width;
@@ -38,10 +40,10 @@ useRender((ctx) => {
currentWidth = props.width;
currentHeight = props.height;
} else {
currentX += dx * props.animationSpeed;
currentY += dy * props.animationSpeed;
currentWidth += dw * props.animationSpeed;
currentHeight += dh * props.animationSpeed;
currentX += dx * ANIMATION_SPEED;
currentY += dy * ANIMATION_SPEED;
currentWidth += dw * ANIMATION_SPEED;
currentHeight += dh * ANIMATION_SPEED;
}
const x = Math.floor(currentX);
@@ -49,6 +51,8 @@ useRender((ctx) => {
const w = Math.floor(currentWidth);
const h = Math.floor(currentHeight);
ctx.globalAlpha = props.opacity;
ctx.drawImage(cornerImage.value, x, y);
ctx.save();

View File

@@ -1,16 +1,31 @@
<script setup lang="ts">
const backgroundImage = useTemplateRef("backgroundImage");
const store = useContactStore();
const homeBackgroundImage = useTemplateRef("homeBackgroundImage");
const contactBackgroundImage = useTemplateRef("contactBackgroundImage");
callOnce("contactIntro", store.animateIntro);
useRender((ctx) => {
if (!backgroundImage.value) return;
if (!homeBackgroundImage.value || !contactBackgroundImage.value) return;
ctx.drawImage(backgroundImage.value, 0, 0);
if (store.isIntro) {
ctx.drawImage(homeBackgroundImage.value, 0, 0);
ctx.globalAlpha = store.intro.stage2Opacity;
}
ctx.drawImage(contactBackgroundImage.value, 0, 0);
});
</script>
<template>
<img
ref="backgroundImage"
ref="homeBackgroundImage"
src="/assets/images/home/bottom-screen/background.png"
hidden
/>
<img
ref="contactBackgroundImage"
src="/assets/images/contact/bottom-screen/background.png"
hidden
/>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
const store = useContactStore();
const topBarImage = useTemplateRef("topBarImage");
const bottomBarImage = useTemplateRef("bottomBarImage");
useRender((ctx) => {
if (!topBarImage.value || !bottomBarImage.value) return;
ctx.globalAlpha = store.isIntro ? store.intro.stage3Opacity : 1;
ctx.drawImage(topBarImage.value, 0, store.isIntro ? store.intro.topBarY : 0);
ctx.drawImage(
bottomBarImage.value,
0,
store.isIntro ? store.intro.bottomBarY : SCREEN_HEIGHT - 24,
);
});
</script>
<template>
<img
ref="topBarImage"
src="/assets/images/contact/bottom-screen/top-bar.png"
hidden
/>
<img
ref="bottomBarImage"
src="/assets/images/contact/bottom-screen/bottom-bar.png"
hidden
/>
</template>

View File

@@ -1,6 +1,10 @@
<script setup lang="ts">
import Background from "./Background.vue";
import Buttons from "./Buttons.vue";
import ButtonSelector from "~/components/Common/ButtonSelector.vue";
import Bars from "./Bars.vue";
const store = useContactStore();
const { selectorPosition } = useButtonNavigation({
buttons: {
@@ -35,10 +39,14 @@ const { selectorPosition } = useButtonNavigation({
<template>
<Background />
<Buttons />
<ButtonSelector
:x="selectorPosition[0]"
:y="selectorPosition[1]"
:width="selectorPosition[2]"
:height="selectorPosition[3]"
:opacity="store.isIntro ? store.intro.stage3Opacity : 1"
/>
<Bars />
</template>

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
const store = useContactStore();
const buttonsImage = useTemplateRef("buttonsImage");
useRender((ctx) => {
if (!buttonsImage.value) return;
ctx.globalAlpha = store.isIntro ? store.intro.stage3Opacity : 1;
ctx.drawImage(buttonsImage.value, 31, 32);
});
</script>
<template>
<img
ref="buttonsImage"
src="/assets/images/contact/bottom-screen/buttons.png"
hidden
/>
</template>

View File

@@ -1,16 +1,31 @@
<script setup lang="ts">
const backgroundImage = useTemplateRef("backgroundImage");
const store = useContactStore();
const homeBackgroundImage = useTemplateRef("homeBackgroundImage");
const contactBackgroundImage = useTemplateRef("contactBackgroundImage");
useRender((ctx) => {
if (!backgroundImage.value) return;
if (!homeBackgroundImage.value || !contactBackgroundImage.value) return;
ctx.drawImage(backgroundImage.value, 0, 0);
ctx.drawImage(contactBackgroundImage.value, 0, 0);
if (store.isIntro) {
ctx.drawImage(homeBackgroundImage.value, 0, 0);
ctx.globalAlpha = store.intro.stage2Opacity;
}
ctx.drawImage(contactBackgroundImage.value, 0, 0);
});
</script>
<template>
<img
ref="backgroundImage"
ref="homeBackgroundImage"
src="/assets/images/home/top-screen/background.png"
hidden
/>
<img
ref="contactBackgroundImage"
src="/assets/images/contact/top-screen/background.png"
hidden
/>

View File

@@ -0,0 +1,29 @@
<script setup lang="ts">
const store = useContactStore();
const backgroundImage = useTemplateRef("backgroundImage");
const thingsImage = useTemplateRef("thingsImage");
useRender((ctx) => {
if (!backgroundImage.value || !thingsImage.value) return;
ctx.globalAlpha = store.isIntro ? store.intro.stage1Opacity : 1;
ctx.drawImage(backgroundImage.value, 0, 0);
ctx.globalAlpha = store.isIntro ? store.intro.stage3Opacity : 1;
ctx.drawImage(thingsImage.value, 0, 0);
});
</script>
<template>
<img
ref="backgroundImage"
src="/assets/images/contact/top-screen/left-bar.png"
hidden
/>
<img
ref="thingsImage"
src="/assets/images/contact/top-screen/left-bar-things.png"
hidden
/>
</template>

View File

@@ -0,0 +1,24 @@
<script setup lang="ts">
const store = useContactStore();
const titleImage = useTemplateRef("titleImage");
useRender((ctx) => {
if (!titleImage.value) return;
ctx.globalAlpha = store.isIntro ? store.intro.stage1Opacity : 1;
ctx.drawImage(
titleImage.value,
21,
store.isIntro ? store.intro.titleY : SCREEN_HEIGHT - 23,
);
});
</script>
<template>
<img
ref="titleImage"
src="/assets/images/contact/top-screen/title.png"
hidden
/>
</template>

View File

@@ -1,7 +1,12 @@
<script setup lang="ts">
import Background from "./Background.vue";
import LeftBar from "./LeftBar.vue";
import Title from "./Title.vue";
</script>
<template>
<Background />
<LeftBar />
<Title />
</template>

74
app/stores/contact.ts Normal file
View File

@@ -0,0 +1,74 @@
import gsap from "gsap";
export const useContactStore = defineStore("contact", {
state: () => ({
intro: {
stage1Opacity: 0,
stage2Opacity: 0,
stage3Opacity: 0,
titleY: SCREEN_HEIGHT,
topBarY: -20,
bottomBarY: SCREEN_HEIGHT + 20,
},
isIntro: false,
}),
actions: {
animateIntro() {
this.isIntro = true;
const start = 3;
gsap.fromTo(
this.intro,
{
stage1Opacity: 0,
titleY: SCREEN_HEIGHT,
},
{
stage1Opacity: 1,
titleY: SCREEN_HEIGHT - 23,
duration: 0.1,
delay: start,
ease: "none",
},
);
gsap.fromTo(
this.intro,
{
stage2Opacity: 0,
},
{
stage2Opacity: 1,
duration: 0.1,
delay: start + 0.15,
ease: "none",
},
);
gsap.fromTo(
this.intro,
{
stage3Opacity: 0,
topBarY: -20,
bottomBarY: SCREEN_HEIGHT - 4,
},
{
stage3Opacity: 1,
topBarY: 0,
bottomBarY: SCREEN_HEIGHT - 24,
duration: 0.1,
delay: start + 0.15 + 0.15,
ease: "none",
onComplete: () => {
this.isIntro = false;
},
},
);
},
},
});