From 6fecb6894182d8d1dd1409bead89249d7e6d98b1 Mon Sep 17 00:00:00 2001 From: Dmitri Date: Tue, 14 Apr 2026 18:40:05 +0400 Subject: [PATCH] changed animations to transitions for better Safari stability --- .../src/components/RuntimePresentation.tsx | 58 ++++++++---- frontend/src/css/main.css | 94 ++++++++++--------- frontend/src/hooks/useBackgroundTransition.ts | 82 +++++++++++++--- 3 files changed, 163 insertions(+), 71 deletions(-) diff --git a/frontend/src/components/RuntimePresentation.tsx b/frontend/src/components/RuntimePresentation.tsx index f0a09e0..a4afcdf 100644 --- a/frontend/src/components/RuntimePresentation.tsx +++ b/frontend/src/components/RuntimePresentation.tsx @@ -242,15 +242,38 @@ export default function RuntimePresentation({ // Use shared background transition hook for crossfade effects // NOTE: fadeOut config is NOT used for video transitions. // Video transitions end instantly (last frame = new page, then overlay removed). - // fadeIn is used for non-video navigation (crossfade 500ms). - const { isFadingIn, onFadeInAnimationEnd, resetFadeIn } = - useBackgroundTransition({ - pageSwitch, - // No fadeOut - video transitions don't use fade - fadeIn: { - hasActiveTransition: Boolean(transitionPreview), - }, - }); + // fadeIn is used for non-video navigation (crossfade 700ms via CSS transitions). + const { + isFadingIn, + crossfadePhase, + onFadeInAnimationEnd, + onTransitionEnd, + resetFadeIn, + } = useBackgroundTransition({ + pageSwitch, + // No fadeOut - video transitions don't use fade + fadeIn: { + hasActiveTransition: Boolean(transitionPreview), + }, + }); + + // Helper to generate crossfade classes for CSS transitions + // Two-phase approach: 'starting' = opacity 0, 'running' = opacity 1 (transition animates) + const getCrossfadeInClasses = () => { + if (!isFadingIn) return ''; + const base = 'crossfade-layer'; + if (crossfadePhase === 'starting') return `${base} crossfade-in-start`; + if (crossfadePhase === 'running') return `${base} crossfade-in-end`; + return ''; + }; + + const getCrossfadeOutClasses = () => { + if (!isFadingIn) return ''; + const base = 'crossfade-layer'; + if (crossfadePhase === 'starting') return `${base} crossfade-out-start`; + if (crossfadePhase === 'running') return `${base} crossfade-out-end`; + return ''; + }; const toggleFullscreen = useCallback(async () => { try { @@ -603,40 +626,43 @@ export default function RuntimePresentation({ )} {/* Previous background overlays - show during loading AND crossfade. - Uses CSS animation for fade-out effect. + Uses CSS transitions for fade-out effect (more reliable in Safari). Cleared by useBackgroundTransition after fade completes. */} {pageSwitch.previousBgImageUrl && (isFadingIn || (pageSwitch.isSwitching && !pageSwitch.isNewBgReady)) && (
)} {pageSwitch.previousBgVideoUrl && (isFadingIn || (pageSwitch.isSwitching && !pageSwitch.isNewBgReady)) && (