201 lines
5.5 KiB
Vue
201 lines
5.5 KiB
Vue
<script setup lang="ts">
|
|
import gsap from "gsap";
|
|
|
|
const { onRender } = useScreen();
|
|
const store = useSettingsStore();
|
|
const { assets } = useAssets();
|
|
|
|
type NotificationData = {
|
|
image: AtlasImage;
|
|
title: string;
|
|
description: string;
|
|
};
|
|
|
|
const menuYOffset = ref(0);
|
|
const submenuYOffset = ref(0);
|
|
const visibleMenuNotification = ref<NotificationData | null>(null);
|
|
const visibleSubmenuNotification = ref<NotificationData | null>(null);
|
|
|
|
const renderNotification = (
|
|
ctx: CanvasRenderingContext2D,
|
|
image: AtlasImage,
|
|
title: string,
|
|
description: string,
|
|
) => {
|
|
assets.images.settings.topScreen.notification.draw(ctx, 0, 0);
|
|
image.draw(ctx, 2, 2);
|
|
|
|
ctx.font = "10px NDS10";
|
|
ctx.fillStyle = "#282828";
|
|
ctx.fillText(title, 52, 13);
|
|
|
|
ctx.fillStyle = "#fbfbfb";
|
|
const lines = description.split("\n");
|
|
const textY = lines.length === 1 ? 36 : 28;
|
|
for (let i = 0; i < lines.length; i += 1) {
|
|
ctx.fillText(lines[i]!, 52, textY + 14 * i);
|
|
}
|
|
};
|
|
|
|
const mainNotification = computed(() => ({
|
|
image: assets.images.settings.topScreen.settings,
|
|
title: $t("settings.title"),
|
|
description: $t("settings.description"),
|
|
}));
|
|
|
|
const IMAGES_MAP: Record<string, AtlasImage> = {
|
|
options: assets.images.settings.topScreen.options.options,
|
|
optionsRenderingMode: assets.images.settings.topScreen.options.renderingMode,
|
|
optionsLanguage: assets.images.settings.topScreen.options.language,
|
|
options2048: assets.images.settings.topScreen.options._2048,
|
|
|
|
clock: assets.images.settings.topScreen.clock.clock,
|
|
clockTime: assets.images.settings.topScreen.clock.time,
|
|
clockDate: assets.images.settings.topScreen.clock.date,
|
|
clockAchievements: assets.images.settings.topScreen.clock.achievements,
|
|
|
|
user: assets.images.settings.topScreen.user.user,
|
|
userUserName: assets.images.settings.topScreen.user.userName,
|
|
userBirthday: assets.images.settings.topScreen.user.birthday,
|
|
userSnake: assets.images.settings.topScreen.user.snake,
|
|
userColor: assets.images.settings.topScreen.user.color,
|
|
|
|
touchScreen: assets.images.settings.topScreen.touchScreen.touchScreen,
|
|
touchScreenTapTap: assets.images.settings.topScreen.touchScreen.touchScreen,
|
|
};
|
|
|
|
const menuNotification = computed(() => {
|
|
if (!store.currentMenu || !store.menuExpanded) return null;
|
|
|
|
return {
|
|
image: IMAGES_MAP[store.currentMenu]!,
|
|
title: $t(`settings.${store.currentMenu}.title`),
|
|
description: $t(`settings.${store.currentMenu}.description`),
|
|
};
|
|
});
|
|
|
|
const submenuNotification = computed(() => {
|
|
if (!store.currentSubMenu) return null;
|
|
|
|
const image = IMAGES_MAP[store.currentSubMenu];
|
|
if (!image) return null;
|
|
|
|
const menuMatch = store.currentSubMenu.match(
|
|
/^(options|clock|user|touchScreen)(.+)$/,
|
|
);
|
|
if (!menuMatch) return null;
|
|
|
|
const [, menu, submenu] = menuMatch;
|
|
const submenuKey = submenu!.charAt(0).toLowerCase() + submenu!.slice(1);
|
|
|
|
return {
|
|
image,
|
|
title: $t(`settings.${menu}.${submenuKey}.title`),
|
|
description: $t(`settings.${menu}.${submenuKey}.description`),
|
|
};
|
|
});
|
|
|
|
const animateNotification = (
|
|
curr: NotificationData | null,
|
|
prev: NotificationData | null,
|
|
visibleRef: Ref<NotificationData | null>,
|
|
offsetRef: Ref<number>,
|
|
) => {
|
|
if (prev !== null && curr === null) {
|
|
// slide down
|
|
visibleRef.value = prev;
|
|
store.animatingNotification = true;
|
|
gsap.fromTo(
|
|
offsetRef,
|
|
{ value: 0 },
|
|
{
|
|
value: 48,
|
|
duration: 0.2,
|
|
ease: "none",
|
|
onComplete: () => {
|
|
visibleRef.value = null;
|
|
setTimeout(() => {
|
|
store.animatingNotification = false;
|
|
}, 50);
|
|
},
|
|
},
|
|
);
|
|
} else if (prev === null && curr !== null) {
|
|
// slide up
|
|
store.animatingNotification = true;
|
|
visibleRef.value = curr;
|
|
gsap.fromTo(
|
|
offsetRef,
|
|
{ value: 48 },
|
|
{
|
|
value: 0,
|
|
duration: 0.2,
|
|
ease: "none",
|
|
onComplete: () => {
|
|
setTimeout(() => {
|
|
store.animatingNotification = false;
|
|
}, 50);
|
|
},
|
|
},
|
|
);
|
|
}
|
|
};
|
|
|
|
watch(menuNotification, (curr, prev) => {
|
|
animateNotification(curr, prev, visibleMenuNotification, menuYOffset);
|
|
});
|
|
|
|
watch(submenuNotification, (curr, prev) => {
|
|
animateNotification(curr, prev, visibleSubmenuNotification, submenuYOffset);
|
|
});
|
|
|
|
onRender((ctx) => {
|
|
const submenuY = 144 + submenuYOffset.value;
|
|
const menuY = visibleSubmenuNotification.value
|
|
? Math.min(144, submenuY - 16) + menuYOffset.value
|
|
: 144 + menuYOffset.value;
|
|
const nextY = visibleMenuNotification.value
|
|
? menuY
|
|
: visibleSubmenuNotification.value
|
|
? submenuY
|
|
: null;
|
|
const mainY =
|
|
nextY !== null
|
|
? Math.min(144, nextY - 16) + store.notificationYOffset
|
|
: 144 + store.notificationYOffset;
|
|
|
|
ctx.translate(0, mainY);
|
|
renderNotification(
|
|
ctx,
|
|
mainNotification.value.image,
|
|
mainNotification.value.title,
|
|
mainNotification.value.description,
|
|
);
|
|
|
|
if (visibleMenuNotification.value) {
|
|
ctx.translate(0, menuY - mainY);
|
|
renderNotification(
|
|
ctx,
|
|
visibleMenuNotification.value.image,
|
|
visibleMenuNotification.value.title,
|
|
visibleMenuNotification.value.description,
|
|
);
|
|
}
|
|
|
|
if (visibleSubmenuNotification.value) {
|
|
const prevY = visibleMenuNotification.value ? menuY : mainY;
|
|
ctx.translate(0, submenuY - prevY);
|
|
renderNotification(
|
|
ctx,
|
|
visibleSubmenuNotification.value.image,
|
|
visibleSubmenuNotification.value.title,
|
|
visibleSubmenuNotification.value.description,
|
|
);
|
|
}
|
|
});
|
|
|
|
defineOptions({
|
|
render: () => null,
|
|
});
|
|
</script>
|