Files
pihkaal-me/app/components/Settings/TopScreen/Notifications.vue

227 lines
6.0 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 menuNotificationVisible = ref(false);
const submenuNotificationVisible = ref(false);
const menuNotificationSnapshot = ref<NotificationData | null>(null);
const submenuNotificationSnapshot = ref<NotificationData | null>(null);
const visibleMenuNotification = computed(() =>
menuNotificationVisible.value
? menuNotification.value
: menuNotificationSnapshot.value,
);
const visibleSubmenuNotification = computed(() =>
submenuNotificationVisible.value
? submenuNotification.value
: submenuNotificationSnapshot.value,
);
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,
visibleFlag: Ref<boolean>,
snapshotRef: Ref<NotificationData | null>,
offsetRef: Ref<number>,
) => {
if (prev !== null && curr === null) {
snapshotRef.value = prev;
visibleFlag.value = false;
store.animatingNotification = true;
gsap.fromTo(
offsetRef,
{ value: 0 },
{
value: 48,
duration: 0.2,
ease: "none",
onComplete: () => {
snapshotRef.value = null;
setTimeout(() => {
store.animatingNotification = false;
}, 50);
},
},
);
} else if (prev === null && curr !== null) {
snapshotRef.value = null;
visibleFlag.value = true;
store.animatingNotification = true;
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,
menuNotificationVisible,
menuNotificationSnapshot,
menuYOffset,
);
});
watch(submenuNotification, (curr, prev) => {
animateNotification(
curr,
prev,
submenuNotificationVisible,
submenuNotificationSnapshot,
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>