diff --git a/frontend/app.vue b/frontend/app.vue index cf073a3..c470eff 100644 --- a/frontend/app.vue +++ b/frontend/app.vue @@ -2702,33 +2702,43 @@ async function animateCalendarZoomIn(sourceElement: HTMLElement | null, ghost: C } async function animateCalendarZoomOut(apply: () => void, resolveTarget: () => HTMLElement | null) { - const viewportRect = getCalendarViewportRect(); - if (!viewportRect) { - apply(); - return; - } - clearCalendarZoomPrime(); calendarZoomBusy.value = true; + clearCalendarZoomOverlay(); try { - primeCalendarRect(viewportRect); calendarZoomGhost.value = zoomGhostForCurrentView(); calendarSceneMasked.value = true; - await flushCalendarZoomStartFrame(); apply(); await nextTick(); - const targetRect = getElementRectInCalendar(resolveTarget()); - if (!targetRect) { - calendarZoomOverlay.value = { - ...calendarZoomOverlay.value, - active: false, - }; + const targetRect = getElementRectInScene(resolveTarget()) ?? fallbackZoomOriginRectInScene(); + const cameraStart = targetRect ? cameraTransformForRect(targetRect) : null; + if (!cameraStart) { + calendarSceneMasked.value = false; return; } - morphCalendarRect(targetRect); - await waitCalendarZoomTransition(); + calendarCameraState.value = { + active: true, + left: cameraStart.left, + top: cameraStart.top, + scale: cameraStart.scale, + durationMs: 0, + }; + await nextTick(); + await nextAnimationFrame(); + calendarSceneRef.value?.getBoundingClientRect(); + calendarSceneMasked.value = false; + await nextAnimationFrame(); + calendarCameraState.value = { + active: true, + left: 0, + top: 0, + scale: 1, + durationMs: CALENDAR_ZOOM_DURATION_MS, + }; + await waitCalendarCameraTransition(); } finally { - clearCalendarZoomOverlay(); + await resetCalendarCamera(); + calendarZoomGhost.value = null; calendarSceneMasked.value = false; calendarZoomBusy.value = false; }