Feat: navigation clavier + routing ancre sur Play
- ArrowRight/Left : navigue entre les jeux aux limites (premier/dernier), passe à la slide prev/next - Ancres URL (#slug) : set à chaque changement de jeu, restaurées au chargement, effacées quand on quitte la slide Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
90f155b679
commit
fdab621b48
1 changed files with 61 additions and 16 deletions
|
|
@ -1,34 +1,57 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import { slides } from '@state/slides.svelte'
|
import { slides } from '@state/slides.svelte'
|
||||||
|
import { slideTo } from '@router'
|
||||||
import ResponsivePicture from '@components/ui/ResponsivePicture.svelte'
|
import ResponsivePicture from '@components/ui/ResponsivePicture.svelte'
|
||||||
|
|
||||||
let { data } = $props()
|
let { data } = $props()
|
||||||
|
|
||||||
// --- Derived ---
|
// --- Derived ---
|
||||||
const isActive = $derived(slides.active?.id === 'jouer')
|
const isActive = $derived(slides.active?.id === 'jouer')
|
||||||
const games = $derived(data?.games ?? [])
|
const games = $derived(data?.games ?? [])
|
||||||
|
const prevSlidePath = $derived(slides.all[slides.activeIndex - 1]?.path ?? null)
|
||||||
|
const nextSlidePath = $derived(slides.all[slides.activeIndex + 1]?.path ?? null)
|
||||||
|
|
||||||
// --- State ---
|
// --- State ---
|
||||||
let currentIndex = $state(0) // index actif (carousel + bg)
|
let currentIndex = $state(0)
|
||||||
let displayedIndex = $state(0) // index du contenu affiché
|
let displayedIndex = $state(0)
|
||||||
let phase = $state('idle') // 'idle' | 'exiting' | 'entering'
|
let phase = $state('idle') // 'idle' | 'exiting' | 'entering'
|
||||||
let slideDir = $state(1) // 1 = click droite (exit gauche), -1 = click gauche (exit droite)
|
let slideDir = $state(1) // 1 = exit gauche, -1 = exit droite
|
||||||
|
|
||||||
// Bg crossfade
|
// Bg crossfade
|
||||||
let prevBgColor = $state(null)
|
let prevBgColor = $state(null)
|
||||||
let bgFading = $state(false)
|
let bgFading = $state(false)
|
||||||
|
|
||||||
const displayedGame = $derived(games[displayedIndex] ?? null)
|
const displayedGame = $derived(games[displayedIndex] ?? null)
|
||||||
|
|
||||||
|
// Capture du hash synchrone avant que tout effect puisse le modifier
|
||||||
|
const initialHash = window.location.hash.slice(1)
|
||||||
|
|
||||||
let t1 = null
|
let t1 = null
|
||||||
let t2 = null
|
let t2 = null
|
||||||
let t3 = null
|
let t3 = null
|
||||||
|
|
||||||
|
// --- Ancres ---
|
||||||
|
function setAnchor(index) {
|
||||||
|
const slug = games[index]?.slug
|
||||||
|
if (!slug) return
|
||||||
|
history.replaceState(null, '', '#' + slug)
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAnchor() {
|
||||||
|
history.replaceState(null, '', window.location.pathname + window.location.search)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restauration depuis l'ancre URL — une seule fois quand games est prêt
|
||||||
|
$effect(() => {
|
||||||
|
if (games.length === 0 || !initialHash) return
|
||||||
|
const idx = games.findIndex(g => g.slug === initialHash)
|
||||||
|
if (idx > 0) currentIndex = idx
|
||||||
|
})
|
||||||
|
|
||||||
function selectGame(i) {
|
function selectGame(i) {
|
||||||
if (i === currentIndex || phase !== 'idle' || !games.length) return
|
if (i === currentIndex || phase !== 'idle' || !games.length) return
|
||||||
|
|
||||||
// slideDir = 1 → clic à droite → content sort par la gauche, entre par la droite
|
|
||||||
slideDir = i > currentIndex ? 1 : -1
|
slideDir = i > currentIndex ? 1 : -1
|
||||||
phase = 'exiting'
|
phase = 'exiting'
|
||||||
|
|
||||||
|
|
@ -36,22 +59,39 @@
|
||||||
clearTimeout(t2)
|
clearTimeout(t2)
|
||||||
clearTimeout(t3)
|
clearTimeout(t3)
|
||||||
|
|
||||||
// Phase 2 (300ms) : swap contenu + carousel + bg simultanés
|
|
||||||
t1 = setTimeout(() => {
|
t1 = setTimeout(() => {
|
||||||
prevBgColor = displayedGame?.backgroundColor ?? null
|
prevBgColor = displayedGame?.backgroundColor ?? null
|
||||||
displayedIndex = i
|
displayedIndex = i
|
||||||
currentIndex = i
|
currentIndex = i
|
||||||
phase = 'entering'
|
phase = 'entering'
|
||||||
bgFading = true
|
bgFading = true
|
||||||
|
setAnchor(i)
|
||||||
|
|
||||||
// Fin du bg fade
|
|
||||||
t3 = setTimeout(() => { bgFading = false }, 500)
|
t3 = setTimeout(() => { bgFading = false }, 500)
|
||||||
|
|
||||||
// Fin de l'entrée
|
|
||||||
t2 = setTimeout(() => { phase = 'idle' }, 350)
|
t2 = setTimeout(() => { phase = 'idle' }, 350)
|
||||||
}, 300)
|
}, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Clavier ---
|
||||||
|
function onKeyDown(e) {
|
||||||
|
if (!isActive) return
|
||||||
|
if (e.key === 'ArrowRight') {
|
||||||
|
e.preventDefault()
|
||||||
|
if (currentIndex < games.length - 1) {
|
||||||
|
selectGame(currentIndex + 1)
|
||||||
|
} else if (nextSlidePath) {
|
||||||
|
slideTo(nextSlidePath)
|
||||||
|
}
|
||||||
|
} else if (e.key === 'ArrowLeft') {
|
||||||
|
e.preventDefault()
|
||||||
|
if (currentIndex > 0) {
|
||||||
|
selectGame(currentIndex - 1)
|
||||||
|
} else if (prevSlidePath) {
|
||||||
|
slideTo(prevSlidePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Réinitialisation quand on quitte la slide
|
// Réinitialisation quand on quitte la slide
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (!isActive) {
|
if (!isActive) {
|
||||||
|
|
@ -62,13 +102,18 @@
|
||||||
displayedIndex = 0
|
displayedIndex = 0
|
||||||
phase = 'idle'
|
phase = 'idle'
|
||||||
bgFading = false
|
bgFading = false
|
||||||
|
clearAnchor()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
onMount(() => () => {
|
onMount(() => {
|
||||||
clearTimeout(t1)
|
window.addEventListener('keydown', onKeyDown)
|
||||||
clearTimeout(t2)
|
return () => {
|
||||||
clearTimeout(t3)
|
window.removeEventListener('keydown', onKeyDown)
|
||||||
|
clearTimeout(t1)
|
||||||
|
clearTimeout(t2)
|
||||||
|
clearTimeout(t3)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue