feat(contact): intro animation
|
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 145 KiB |
BIN
app/assets/images/contact/bottom-screen/bottom-bar.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
app/assets/images/contact/bottom-screen/buttons.png
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
app/assets/images/contact/bottom-screen/top-bar.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 193 KiB |
BIN
app/assets/images/contact/top-screen/left-bar-things.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
app/assets/images/contact/top-screen/left-bar.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
app/assets/images/contact/top-screen/title.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
@@ -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();
|
||||
|
||||
@@ -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
|
||||
/>
|
||||
|
||||
32
app/components/Contact/BottomScreen/Bars.vue
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
20
app/components/Contact/BottomScreen/Buttons.vue
Normal 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>
|
||||
@@ -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
|
||||
/>
|
||||
|
||||
29
app/components/Contact/TopScreen/LeftBar.vue
Normal 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>
|
||||
24
app/components/Contact/TopScreen/Title.vue
Normal 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>
|
||||
@@ -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
@@ -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;
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||