function progress_tracker(){ const sections = Array.from(document.querySelectorAll('section[chapter]')); const fill = document.getElementById('progressFill'); const titleEl = document.getElementById('sectionTitle'); const countEl = document.getElementById('sectionCount'); let ticking = false; let currentIndex = -1; function updateProgress() { const viewportH = window.innerHeight; const scrollY = window.scrollY; // 1. Trouver la section active : celle dont le haut est le plus proche // au-dessus (ou sur) le haut du viewport, mais qui n'est pas encore // entièrement passée. let activeIndex = 0; for (let i = 0; i < sections.length; i++) { const rect = sections[i].getBoundingClientRect(); // Une section devient "active" dès que son haut passe sous ~35% du viewport if (rect.top <= viewportH * 0.35) { activeIndex = i; } } // 2. Calculer la progression DANS cette section (0 à 100%) const activeSection = sections[activeIndex]; const rect = activeSection.getBoundingClientRect(); const sectionHeight = activeSection.offsetHeight; // distance défilée depuis le haut de la section const scrolledIntoSection = -rect.top; // on borne entre 0 et la hauteur totale de la section moins le viewport // (pour atteindre 100% quand le bas de la section touche le bas de l'écran) const scrollableDistance = Math.max(sectionHeight - viewportH, 1); let progress = scrolledIntoSection / scrollableDistance; progress = Math.min(Math.max(progress, 0), 1); // Cas particulier : si la section est plus petite que le viewport, // on considère 100% dès qu'elle est visible en haut. if (sectionHeight <= viewportH) { progress = scrolledIntoSection > 0 ? 1 : 0; } fill.style.width = (progress * 100).toFixed(1) + '%'; // 3. Mettre à jour le titre uniquement si la section a changé // (évite de re-déclencher la transition CSS à chaque pixel) if (activeIndex !== currentIndex) { currentIndex = activeIndex; titleEl.style.opacity = 0; setTimeout(() => { titleEl.textContent = activeSection.dataset.title; countEl.textContent = (activeIndex + 1) + ' / ' + sections.length; titleEl.style.opacity = 1; }, 120); } } function onScroll() { if (!ticking) { requestAnimationFrame(() => { updateProgress(); ticking = false; }); ticking = true; } } window.addEventListener('scroll', onScroll, { passive: true }); window.addEventListener('resize', onScroll); updateProgress(); // init au chargement })();