feat(settings): menu animations
BIN
app/assets/images/settings/top-screen/clock/clock-active.webp
Normal file
|
After Width: | Height: | Size: 322 B |
BIN
app/assets/images/settings/top-screen/clock/clock-disabled.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
app/assets/images/settings/top-screen/options/options-active.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 254 B After Width: | Height: | Size: 254 B |
BIN
app/assets/images/settings/top-screen/user/user-active.webp
Normal file
|
After Width: | Height: | Size: 302 B |
BIN
app/assets/images/settings/top-screen/user/user-disabled.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 344 B After Width: | Height: | Size: 344 B |
@@ -1,5 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import MENU_IMAGE from "/assets/images/settings/top-screen/clock/clock.webp";
|
import MENU_IMAGE from "/assets/images/settings/top-screen/clock/clock.webp";
|
||||||
|
import MENU_ACTIVE_IMAGE from "/assets/images/settings/top-screen/clock/clock-active.webp";
|
||||||
|
import MENU_DISABLED_IMAGE from "/assets/images/settings/top-screen/clock/clock-disabled.png";
|
||||||
import ALARM_IMAGE from "/assets/images/settings/top-screen/clock/alarm.webp";
|
import ALARM_IMAGE from "/assets/images/settings/top-screen/clock/alarm.webp";
|
||||||
import TIME_IMAGE from "/assets/images/settings/top-screen/clock/time.webp";
|
import TIME_IMAGE from "/assets/images/settings/top-screen/clock/time.webp";
|
||||||
import DATE_IMAGE from "/assets/images/settings/top-screen/clock/date.webp";
|
import DATE_IMAGE from "/assets/images/settings/top-screen/clock/date.webp";
|
||||||
@@ -8,24 +10,48 @@ const props = defineProps<{
|
|||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
isAnyOtherMenuOpen: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const [menuImage, alarmImage, timeImage, dateImage] = useImages(
|
const [
|
||||||
|
menuImage,
|
||||||
|
menuActiveImage,
|
||||||
|
menuDisabledImage,
|
||||||
|
alarmImage,
|
||||||
|
timeImage,
|
||||||
|
dateImage,
|
||||||
|
] = useImages(
|
||||||
MENU_IMAGE,
|
MENU_IMAGE,
|
||||||
|
MENU_ACTIVE_IMAGE,
|
||||||
|
MENU_DISABLED_IMAGE,
|
||||||
ALARM_IMAGE,
|
ALARM_IMAGE,
|
||||||
TIME_IMAGE,
|
TIME_IMAGE,
|
||||||
DATE_IMAGE,
|
DATE_IMAGE,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const animation = useMenuAnimation(toRef(() => props.isOpen));
|
||||||
|
|
||||||
useRender((ctx) => {
|
useRender((ctx) => {
|
||||||
ctx.translate(props.x, props.y);
|
ctx.translate(props.x, props.y);
|
||||||
|
|
||||||
ctx.drawImage(menuImage!, 0, 0);
|
if (props.isOpen || animation.playing) {
|
||||||
|
ctx.drawImage(
|
||||||
|
timeImage!,
|
||||||
|
48 - animation.stage2Offset,
|
||||||
|
-48 + animation.stage1Offset,
|
||||||
|
);
|
||||||
|
ctx.drawImage(
|
||||||
|
dateImage!,
|
||||||
|
0,
|
||||||
|
-96 + animation.stage2Offset + animation.stage1Offset,
|
||||||
|
);
|
||||||
|
ctx.drawImage(alarmImage!, 0, -48 + animation.stage1Offset);
|
||||||
|
|
||||||
if (props.isOpen) {
|
ctx.drawImage(menuActiveImage!, 0, 0);
|
||||||
ctx.drawImage(alarmImage!, 0, -48);
|
} else if (props.isAnyOtherMenuOpen) {
|
||||||
ctx.drawImage(timeImage!, 48, -48);
|
ctx.drawImage(menuDisabledImage!, 0, 0);
|
||||||
ctx.drawImage(dateImage!, 0, -96);
|
} else {
|
||||||
|
ctx.drawImage(menuImage!, 0, 0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -98,13 +98,44 @@ const { selectedButton: selected, selectorPosition } = useButtonNavigation({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isMenuOpen = (menu: string) => {
|
||||||
|
const regex = new RegExp(`^${menu}[A-Z]`);
|
||||||
|
return regex.test(selected.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isAnyOtherMenuOpen = (excludeMenu: string) => {
|
||||||
|
return ["options", "clock", "user"]
|
||||||
|
.filter((menu) => menu !== excludeMenu)
|
||||||
|
.some((menu) => isMenuOpen(menu));
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<OptionsMenu :x="33" :y="121" :is-open="/^options[A-Z]/.test(selected)" />
|
<OptionsMenu
|
||||||
<ClockMenu :x="81" :y="121" :is-open="/^clock[A-Z]/.test(selected)" />
|
:x="33"
|
||||||
<UserMenu :x="129" :y="121" :is-open="/^user[A-Z]/.test(selected)" />
|
:y="121"
|
||||||
<TouchScreenMenu :x="177" :y="121" :opacity="1" />
|
:is-open="isMenuOpen('options')"
|
||||||
|
:is-any-other-menu-open="isAnyOtherMenuOpen('options')"
|
||||||
|
/>
|
||||||
|
<ClockMenu
|
||||||
|
:x="81"
|
||||||
|
:y="121"
|
||||||
|
:is-open="isMenuOpen('clock')"
|
||||||
|
:is-any-other-menu-open="isAnyOtherMenuOpen('clock')"
|
||||||
|
/>
|
||||||
|
<UserMenu
|
||||||
|
:x="129"
|
||||||
|
:y="121"
|
||||||
|
:is-open="isMenuOpen('user')"
|
||||||
|
:is-any-other-menu-open="isAnyOtherMenuOpen('user')"
|
||||||
|
/>
|
||||||
|
<TouchScreenMenu
|
||||||
|
:x="177"
|
||||||
|
:y="121"
|
||||||
|
:opacity="1"
|
||||||
|
:is-any-other-menu-open="isAnyOtherMenuOpen('touchScreen')"
|
||||||
|
/>
|
||||||
|
|
||||||
<Selector :rect="selectorPosition" :opacity="1" />
|
<Selector :rect="selectorPosition" :opacity="1" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import MENU_IMAGE from "/assets/images/settings/top-screen/options/options.webp";
|
import MENU_IMAGE from "/assets/images/settings/top-screen/options/options.webp";
|
||||||
|
import MENU_ACTIVE_IMAGE from "/assets/images/settings/top-screen/options/options-active.png";
|
||||||
|
import MENU_DISABLED_IMAGE from "/assets/images/settings/top-screen/options/options-disabled.png";
|
||||||
import GBA_MODE_IMAGE from "/assets/images/settings/top-screen/options/gba-mode.webp";
|
import GBA_MODE_IMAGE from "/assets/images/settings/top-screen/options/gba-mode.webp";
|
||||||
import LANGUAGE_IMAGE from "/assets/images/settings/top-screen/options/language.webp";
|
import LANGUAGE_IMAGE from "/assets/images/settings/top-screen/options/language.webp";
|
||||||
import START_UP_IMAGE from "/assets/images/settings/top-screen/options/start-up.webp";
|
import START_UP_IMAGE from "/assets/images/settings/top-screen/options/start-up.webp";
|
||||||
@@ -8,24 +10,48 @@ const props = defineProps<{
|
|||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
isAnyOtherMenuOpen: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const [menuImage, gbaModeImage, languageImage, startUpImage] = useImages(
|
const [
|
||||||
|
menuImage,
|
||||||
|
menuActiveImage,
|
||||||
|
menuDisabledImage,
|
||||||
|
gbaModeImage,
|
||||||
|
languageImage,
|
||||||
|
startUpImage,
|
||||||
|
] = useImages(
|
||||||
MENU_IMAGE,
|
MENU_IMAGE,
|
||||||
|
MENU_ACTIVE_IMAGE,
|
||||||
|
MENU_DISABLED_IMAGE,
|
||||||
GBA_MODE_IMAGE,
|
GBA_MODE_IMAGE,
|
||||||
LANGUAGE_IMAGE,
|
LANGUAGE_IMAGE,
|
||||||
START_UP_IMAGE,
|
START_UP_IMAGE,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const animation = useMenuAnimation(toRef(() => props.isOpen));
|
||||||
|
|
||||||
useRender((ctx) => {
|
useRender((ctx) => {
|
||||||
ctx.translate(props.x, props.y);
|
ctx.translate(props.x, props.y);
|
||||||
|
|
||||||
ctx.drawImage(menuImage!, 0, 0);
|
if (props.isOpen || animation.playing) {
|
||||||
|
ctx.drawImage(languageImage!, 0, -48 + animation.stage1Offset);
|
||||||
|
ctx.drawImage(
|
||||||
|
gbaModeImage!,
|
||||||
|
48 - animation.stage2Offset,
|
||||||
|
-48 + animation.stage1Offset,
|
||||||
|
);
|
||||||
|
ctx.drawImage(
|
||||||
|
startUpImage!,
|
||||||
|
0,
|
||||||
|
-96 + animation.stage2Offset + animation.stage1Offset,
|
||||||
|
);
|
||||||
|
|
||||||
if (props.isOpen) {
|
ctx.drawImage(menuActiveImage!, 0, 0);
|
||||||
ctx.drawImage(languageImage!, 0, -48);
|
} else if (props.isAnyOtherMenuOpen) {
|
||||||
ctx.drawImage(gbaModeImage!, 48, -48);
|
ctx.drawImage(menuDisabledImage!, 0, 0);
|
||||||
ctx.drawImage(startUpImage!, 0, -96);
|
} else {
|
||||||
|
ctx.drawImage(menuImage!, 0, 0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import MENU_IMAGE from "/assets/images/settings/top-screen/touch_screen/touch_screen.webp";
|
import MENU_IMAGE from "/assets/images/settings/top-screen/touch_screen/touch-screen.webp";
|
||||||
|
import MENU_DISABLED_IMAGE from "/assets/images/settings/top-screen/touch_screen/touch-screen-disabled.png";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
isAnyOtherMenuOpen: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const [menuImage] = useImages(MENU_IMAGE);
|
const [menuImage, menuDisabledImage] = useImages(
|
||||||
|
MENU_IMAGE,
|
||||||
|
MENU_DISABLED_IMAGE,
|
||||||
|
);
|
||||||
|
|
||||||
useRender((ctx) => {
|
useRender((ctx) => {
|
||||||
|
if (props.isAnyOtherMenuOpen) {
|
||||||
|
ctx.drawImage(menuDisabledImage!, props.x, props.y);
|
||||||
|
} else {
|
||||||
ctx.drawImage(menuImage!, props.x, props.y);
|
ctx.drawImage(menuImage!, props.x, props.y);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
|
|||||||
@@ -1,35 +1,65 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import MENU_IMAGE from "/assets/images/settings/top-screen/user/user.webp";
|
import MENU_IMAGE from "/assets/images/settings/top-screen/user/user.webp";
|
||||||
|
import MENU_ACTIVE_IMAGE from "/assets/images/settings/top-screen/user/user-active.webp";
|
||||||
|
import MENU_DISABLED_IMAGE from "/assets/images/settings/top-screen/user/user-disabled.png";
|
||||||
import BIRTHDAY_IMAGE from "/assets/images/settings/top-screen/user/birthday.webp";
|
import BIRTHDAY_IMAGE from "/assets/images/settings/top-screen/user/birthday.webp";
|
||||||
import COLOR_IMAGE from "/assets/images/settings/top-screen/user/color.webp";
|
import COLOR_IMAGE from "/assets/images/settings/top-screen/user/color.webp";
|
||||||
import MESSAGE_IMAGE from "/assets/images/settings/top-screen/user/message.webp";
|
import MESSAGE_IMAGE from "/assets/images/settings/top-screen/user/message.webp";
|
||||||
import USER_NAME_IMAGE from "/assets/images/settings/top-screen/user/user_name.webp";
|
import USER_NAME_IMAGE from "/assets/images/settings/top-screen/user/user-name.webp";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
isAnyOtherMenuOpen: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const [menuImage, birthdayImage, colorImage, messageImage, userNameImage] =
|
const [
|
||||||
useImages(
|
menuImage,
|
||||||
|
menuActiveImage,
|
||||||
|
menuDisabledImage,
|
||||||
|
birthdayImage,
|
||||||
|
colorImage,
|
||||||
|
messageImage,
|
||||||
|
userNameImage,
|
||||||
|
] = useImages(
|
||||||
MENU_IMAGE,
|
MENU_IMAGE,
|
||||||
|
MENU_ACTIVE_IMAGE,
|
||||||
|
MENU_DISABLED_IMAGE,
|
||||||
BIRTHDAY_IMAGE,
|
BIRTHDAY_IMAGE,
|
||||||
COLOR_IMAGE,
|
COLOR_IMAGE,
|
||||||
MESSAGE_IMAGE,
|
MESSAGE_IMAGE,
|
||||||
USER_NAME_IMAGE,
|
USER_NAME_IMAGE,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const animation = useMenuAnimation(toRef(() => props.isOpen));
|
||||||
|
|
||||||
useRender((ctx) => {
|
useRender((ctx) => {
|
||||||
ctx.translate(props.x, props.y);
|
ctx.translate(props.x, props.y);
|
||||||
|
|
||||||
ctx.drawImage(menuImage!, 0, 0);
|
if (props.isOpen || animation.playing) {
|
||||||
|
ctx.drawImage(
|
||||||
|
birthdayImage!,
|
||||||
|
-48 + animation.stage2Offset,
|
||||||
|
-48 + animation.stage1Offset,
|
||||||
|
);
|
||||||
|
ctx.drawImage(userNameImage!, 0, -48 + animation.stage1Offset);
|
||||||
|
ctx.drawImage(
|
||||||
|
messageImage!,
|
||||||
|
48 - animation.stage2Offset,
|
||||||
|
-48 + animation.stage1Offset,
|
||||||
|
);
|
||||||
|
ctx.drawImage(
|
||||||
|
colorImage!,
|
||||||
|
0,
|
||||||
|
-96 + animation.stage2Offset + animation.stage1Offset,
|
||||||
|
);
|
||||||
|
|
||||||
if (props.isOpen) {
|
ctx.drawImage(menuActiveImage!, 0, 0);
|
||||||
ctx.drawImage(birthdayImage!, -48, -48);
|
} else if (props.isAnyOtherMenuOpen) {
|
||||||
ctx.drawImage(userNameImage!, 0, -48);
|
ctx.drawImage(menuDisabledImage!, 0, 0);
|
||||||
ctx.drawImage(messageImage!, 48, -48);
|
} else {
|
||||||
ctx.drawImage(colorImage!, 0, -96);
|
ctx.drawImage(menuImage!, 0, 0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
33
app/composables/useMenuAnimation.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import gsap from "gsap";
|
||||||
|
|
||||||
|
export const useMenuAnimation = (isOpen: Ref<boolean>) => {
|
||||||
|
const animation = reactive({
|
||||||
|
playing: false,
|
||||||
|
stage1Offset: 48,
|
||||||
|
stage2Offset: 48,
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(isOpen, (current, previous) => {
|
||||||
|
const duration = 0.1;
|
||||||
|
const timeline = gsap.timeline({
|
||||||
|
onStart: () => {
|
||||||
|
animation.playing = true;
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
animation.playing = false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (current === true && previous === false) {
|
||||||
|
timeline
|
||||||
|
.fromTo(animation, { stage1Offset: 48 }, { stage1Offset: 0, duration })
|
||||||
|
.fromTo(animation, { stage2Offset: 48 }, { stage2Offset: 0, duration });
|
||||||
|
} else if (current === false && previous === true) {
|
||||||
|
timeline
|
||||||
|
.fromTo(animation, { stage2Offset: 0 }, { stage2Offset: 48, duration })
|
||||||
|
.fromTo(animation, { stage1Offset: 0 }, { stage1Offset: 48, duration });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return animation;
|
||||||
|
};
|
||||||