feat(settings): intro and outro animation

This commit is contained in:
2026-02-05 20:44:19 +01:00
parent 098285ee82
commit 1236e86981
10 changed files with 306 additions and 52 deletions

View File

@@ -1,13 +1,14 @@
<script setup lang="ts">
const { onRender } = useScreen();
const store = useSettingsStore();
const { assets } = useAssets();
onRender((ctx) => {
ctx.fillStyle = "black";
ctx.font = "7px NDS7";
ctx.translate(0, -16);
ctx.translate(0, -16 + store.notificationYOffset / 3);
const CALENDAR_COLS = 7;
const CALENDAR_ROWS = 5;

View File

@@ -2,6 +2,7 @@
const { onRender } = useScreen();
const app = useAppStore();
const store = useSettingsStore();
const { assets } = useAssets();
const CENTER_X = 63;
@@ -53,7 +54,7 @@ function drawLine(
}
onRender((ctx) => {
ctx.translate(0, -16);
ctx.translate(0, -16 + store.notificationYOffset / 3);
assets.images.home.topScreen.clock.draw(ctx, 13, 45);

View File

@@ -1,9 +1,21 @@
<script setup lang="ts">
const { onRender } = useScreen();
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,
@@ -83,13 +95,62 @@ const submenuNotification = computed(() => {
};
});
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;
gsap.fromTo(
offsetRef,
{ value: 0 },
{
value: 48,
duration: 0.2,
ease: "none",
onComplete: () => {
visibleRef.value = null;
},
},
);
} else if (prev === null && curr !== null) {
// slide up
visibleRef.value = curr;
gsap.fromTo(
offsetRef,
{ value: 48 },
{ value: 0, duration: 0.2, ease: "none" },
);
}
};
watch(menuNotification, (curr, prev) => {
animateNotification(curr, prev, visibleMenuNotification, menuYOffset);
});
watch(submenuNotification, (curr, prev) => {
animateNotification(curr, prev, visibleSubmenuNotification, submenuYOffset);
});
onRender((ctx) => {
let count = 1;
if (menuNotification.value) count++;
if (submenuNotification.value) count++;
ctx.translate(0, 144 - (count - 1) * 16);
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,
@@ -97,23 +158,24 @@ onRender((ctx) => {
mainNotification.value.description,
);
if (menuNotification.value) {
ctx.translate(0, 16);
if (visibleMenuNotification.value) {
ctx.translate(0, menuY - mainY);
renderNotification(
ctx,
menuNotification.value.image,
menuNotification.value.title,
menuNotification.value.description,
visibleMenuNotification.value.image,
visibleMenuNotification.value.title,
visibleMenuNotification.value.description,
);
}
if (submenuNotification.value) {
ctx.translate(0, 16);
if (visibleSubmenuNotification.value) {
const prevY = visibleMenuNotification.value ? menuY : mainY;
ctx.translate(0, submenuY - prevY);
renderNotification(
ctx,
submenuNotification.value.image,
submenuNotification.value.title,
submenuNotification.value.description,
visibleSubmenuNotification.value.image,
visibleSubmenuNotification.value.title,
visibleSubmenuNotification.value.description,
);
}
});

View File

@@ -4,6 +4,12 @@ import Calendar from "./Calendar.vue";
import Clock from "./Clock.vue";
import StatusBar from "./StatusBar.vue";
import Notifications from "./Notifications.vue";
const store = useSettingsStore();
onMounted(() => {
store.animateIntro();
});
</script>
<template>