diff --git a/src/App.svelte b/src/App.svelte index 4967b2f..6c6d110 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -38,10 +38,11 @@ let resizeTimer = null // Active la transition seulement après le premier paint à la bonne position. - // Sans ça, un chargement sur /expertise slide depuis l'accueil. + // Double rAF : le premier laisse passer un paint avec le bon translateX, + // le second active is-animated — évite l'animation parasite au chargement. $effect(() => { if (slides.all.length > 0 && !isReady) { - requestAnimationFrame(() => { isReady = true }) + requestAnimationFrame(() => requestAnimationFrame(() => { isReady = true })) } }) diff --git a/src/views/Portfolio.svelte b/src/views/Portfolio.svelte index a23a142..b5ef44b 100644 --- a/src/views/Portfolio.svelte +++ b/src/views/Portfolio.svelte @@ -11,10 +11,31 @@ let sectionEl = $state(null) // --- Derived --- - const isActive = $derived(slides.active?.id === 'portfolio') - const projects = $derived(data?.projects ?? []) + const isActive = $derived(slides.active?.id === 'portfolio') + const projects = $derived(data?.projects ?? []) const currentProject = $derived(projects[currentIndex] ?? null) + // Capture du hash synchrone avant que tout effect puisse le modifier + const initialHash = window.location.hash.slice(1) + + // --- Ancres --- + function setAnchor(index) { + const slug = projects[index]?.slug + if (!slug) return + history.replaceState(null, '', '#' + slug) + } + + function clearAnchor() { + history.replaceState(null, '', window.location.pathname + window.location.search) + } + + // Initialisation depuis l'ancre URL — une seule fois quand projects est prêt + $effect(() => { + if (projects.length === 0 || !initialHash) return + const idx = projects.findIndex(p => p.slug === initialHash) + if (idx > 0) currentIndex = idx + }) + // --- Scroll nav composable --- const nav = createScrollNav({ isActive: () => isActive, @@ -24,6 +45,7 @@ : Math.max(currentIndex - 1, 0) if (next === currentIndex) return false currentIndex = next + setAnchor(next) }, }) @@ -40,10 +62,17 @@ }) // --- Effect: reset when slide deactivated --- + // wasActive évite que clearAnchor() s'exécute au montage initial + // (isActive est false avant l'initialisation des slides) + let wasActive = false $effect(() => { - if (!isActive) { + if (isActive) { + wasActive = true + } else if (wasActive) { nav.reset() currentIndex = 0 + clearAnchor() + wasActive = false } }) @@ -97,7 +126,7 @@