From 600ce937a3c7b78ca3151f0b3afcfebf97c2f584 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Mon, 9 Mar 2026 19:24:41 +0100 Subject: [PATCH] =?UTF-8?q?Feat:=20transitions=20Play=20soign=C3=A9es=20?= =?UTF-8?q?=E2=80=94=20exit/enter=20directionnel=20+=20bg=20crossfade=20+?= =?UTF-8?q?=20game-preview?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Phase exiting (300ms) : featured + game-preview glissent/fondent dans slideDir - Phase entering (350ms) : nouvel contenu entre depuis la direction opposée - Swap currentIndex à 300ms : carousel grossit + bg crossfade simultanés - Ajout game-preview (image preview côté droit, grid-area 3/13) - Ajout preview dans le template JSON PHP - Masqué sur mobile Co-Authored-By: Claude Sonnet 4.6 --- site/templates/play.json.php | 1 + src/views/Play.svelte | 191 ++++++++++++++++++++++++----------- 2 files changed, 133 insertions(+), 59 deletions(-) diff --git a/site/templates/play.json.php b/site/templates/play.json.php index 5a92136..feeb766 100644 --- a/site/templates/play.json.php +++ b/site/templates/play.json.php @@ -9,6 +9,7 @@ $specificData = [ 'description' => $game->description()->value(), 'thumbnail' => $game->thumbnail()->toFile()?->url(), 'backgroundColor' => $game->backgroundColor()->value() ?: null, + 'preview' => $game->preview()->toFile()?->url(), 'playLink' => $game->playLink()->value() ?: null, ]; })->values() diff --git a/src/views/Play.svelte b/src/views/Play.svelte index aabb533..3700179 100644 --- a/src/views/Play.svelte +++ b/src/views/Play.svelte @@ -9,38 +9,46 @@ const games = $derived(data?.games ?? []) // --- State --- - let currentIndex = $state(0) // index de la sélection (mis à jour immédiatement) - let displayedIndex = $state(0) // index affiché (mis à jour au milieu de la transition) - let isOut = $state(false) - let isTransitioning = $state(false) - let slideDir = $state(1) // -1 : vers la droite, 1 : vers la gauche + let currentIndex = $state(0) // index actif (carousel + bg) + let displayedIndex = $state(0) // index du contenu affiché + let phase = $state('idle') // 'idle' | 'exiting' | 'entering' + let slideDir = $state(1) // 1 = click droite (exit gauche), -1 = click gauche (exit droite) + + // Bg crossfade + let prevBgColor = $state(null) + let bgFading = $state(false) const displayedGame = $derived(games[displayedIndex] ?? null) let t1 = null let t2 = null + let t3 = null function selectGame(i) { - if (i === currentIndex || isTransitioning || !games.length) return + if (i === currentIndex || phase !== 'idle' || !games.length) return - slideDir = i > currentIndex ? -1 : 1 - currentIndex = i - isOut = true - isTransitioning = true + // slideDir = 1 → clic à droite → content sort par la gauche, entre par la droite + slideDir = i > currentIndex ? 1 : -1 + phase = 'exiting' clearTimeout(t1) clearTimeout(t2) + clearTimeout(t3) - // Milieu : swap du contenu affiché et du fond (éléments invisibles à ce moment) + // Phase 2 (300ms) : swap contenu + carousel + bg simultanés t1 = setTimeout(() => { + prevBgColor = displayedGame?.backgroundColor ?? null displayedIndex = i - isOut = false - }, 300) + currentIndex = i + phase = 'entering' + bgFading = true - // Fin de transition - t2 = setTimeout(() => { - isTransitioning = false - }, 600) + // Fin du bg fade + t3 = setTimeout(() => { bgFading = false }, 500) + + // Fin de l'entrée + t2 = setTimeout(() => { phase = 'idle' }, 350) + }, 300) } // Réinitialisation quand on quitte la slide @@ -48,16 +56,18 @@ if (!isActive) { clearTimeout(t1) clearTimeout(t2) - currentIndex = 0 - displayedIndex = 0 - isOut = false - isTransitioning = false + clearTimeout(t3) + currentIndex = 0 + displayedIndex = 0 + phase = 'idle' + bgFading = false } }) onMount(() => () => { clearTimeout(t1) clearTimeout(t2) + clearTimeout(t3) }) @@ -67,9 +77,13 @@ style={displayedGame?.backgroundColor ? `--background-color: ${displayedGame.backgroundColor}` : ''} > - - + + {#if bgFading && prevBgColor} + + {/if} + + + + + {#if displayedGame?.preview} + + {/if} - +