import { initSwipers } from './swipers.js'; export function report(responsiveSmall) { if (document.body.dataset.template === 'report') { // Initialiser tous les sliders de type before-after initSliderBeforeAfter(); // Ne fonctionne que pour les écrans plus grands que responsiveSmall if (window.matchMedia(responsiveSmall).matches) { // Sur mobile : initialiser les swipers normalement car initMediaDisplay ne sera pas actif initSwipers(); return; } // Sur desktop : initMediaDisplay va gérer les media dynamiquement // Les swipers seront initialisés au moment de l'insertion dans #report__medias initMediaDisplay(); } } function initSliderBeforeAfter(container = document){ const slidersBeforeAfter = container.querySelectorAll('.slider-before-after'); slidersBeforeAfter.forEach(function (sliderContainer, index) { const sliderInput = sliderContainer.querySelector('.slider'); if (sliderInput) { sliderInput.addEventListener('input', (e) => { console.log('slider value:', e.target.value); sliderContainer.style.setProperty('--position', `${e.target.value}%`); }); } }); } function initMediaDisplay() { const reportMedias = document.querySelector('#report__medias'); if (!reportMedias) return; // Calculer la hauteur depuis les variables CSS: calc(var(--header-h) + var(--padding-body)) const rootStyles = getComputedStyle(document.documentElement); const headerH = rootStyles.getPropertyValue('--header-h').trim(); const paddingBody = rootStyles.getPropertyValue('--padding-body').trim(); const spacingH = rootStyles.getPropertyValue('--spacing').trim(); // Convertir en pixels si nécessaire const headerHPx = parseFloat(headerH); const paddingBodyPx = parseFloat(paddingBody); const spacingHPx = parseFloat(paddingBody); const totalOffset = headerHPx + paddingBodyPx + spacingHPx*5; const sections = document.querySelectorAll('.section-content'); const mediaElements = []; let mediaCounter = 0; // 1. Pour chaque section, traiter les .media sections.forEach((section) => { const medias = section.querySelectorAll('.media'); medias.forEach((media) => { // Générer un ID unique si nécessaire if (!media.id) { media.id = `media-${mediaCounter++}`; } // Créer une ancre const anchor = document.createElement('div'); anchor.className = 'media-anchor'; anchor.dataset.mediaId = media.id; anchor.innerHTML = '' // Vérifier si le media est précédé d'un titre let previousElement = media.previousElementSibling; let insertBeforeElement = media; // Si l'élément précédent est un titre (h1-h6), insérer l'ancre avant le titre if (previousElement && /^H[1-6]$/.test(previousElement.tagName)) { insertBeforeElement = previousElement; } // Insérer l'ancre insertBeforeElement.parentNode.insertBefore(anchor, insertBeforeElement); // Stocker la référence pour l'observer mediaElements.push({ anchor: anchor, media: media.cloneNode(true), // Cloner le media originalMedia: media, section: section }); // Masquer le media original media.style.display = 'none'; }); }); // 2. Fonction pour trouver et afficher le media le plus proche de la ligne de déclenchement let currentMediaId = null; let isUpdating = false; // Flag pour éviter les mises à jour simultanées function updateActiveMedia() { // Éviter les mises à jour simultanées if (isUpdating) return; // Trouver l'ancre qui est la plus proche de la ligne de déclenchement (totalOffset du haut) let closestAnchor = null; let closestDistance = Infinity; mediaElements.forEach(({ anchor }) => { const rect = anchor.getBoundingClientRect(); // Si l'ancre est au-dessus ou à la ligne de déclenchement if (rect.top <= totalOffset) { const distance = totalOffset - rect.top; // Prendre celle qui vient juste de passer (la plus proche en dessous de la ligne) if (distance < closestDistance) { closestDistance = distance; closestAnchor = anchor; } } }); // Si on a trouvé une ancre if (closestAnchor) { const mediaId = closestAnchor.dataset.mediaId; // Si c'est déjà le media affiché, ne rien faire if (currentMediaId === mediaId) return; // Trouver le media correspondant const mediaData = mediaElements.find(m => m.anchor === closestAnchor); if (mediaData) { isUpdating = true; // Utiliser requestAnimationFrame pour éviter les conflits de reflow requestAnimationFrame(() => { // Vider le conteneur reportMedias.innerHTML = ''; // Ajouter le nouveau media const newMediaElement = mediaData.media.cloneNode(true); reportMedias.appendChild(newMediaElement); currentMediaId = mediaId; // Attendre le prochain frame pour initialiser les sliders/swipers requestAnimationFrame(() => { initSliderBeforeAfter(reportMedias); initSwipers(reportMedias); // Débloquer les mises à jour après un court délai setTimeout(() => { isUpdating = false; }, 100); }); }); } } else { // Aucune ancre n'a encore franchi la ligne, vider le conteneur if (currentMediaId !== null) { reportMedias.innerHTML = ''; currentMediaId = null; } } } // 3. Écouter le scroll let scrollTimeout; window.addEventListener('scroll', () => { // Throttle pour optimiser les performances if (scrollTimeout) return; scrollTimeout = setTimeout(() => { updateActiveMedia(); scrollTimeout = null; }, 10); }); // Appeler une première fois au chargement updateActiveMedia(); // 4. Gérer les sections sans media immédiat // Observer aussi les sections elles-mêmes const sectionObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const section = entry.target; // Vérifier si cette section a un media juste après son premier titre const firstTitle = section.querySelector('h1, h2, h3, h4, h5, h6'); if (firstTitle) { let nextElement = firstTitle.nextElementSibling; // Chercher le prochain élément qui n'est pas une ancre while (nextElement && nextElement.classList.contains('media-anchor')) { nextElement = nextElement.nextElementSibling; } // Si le prochain élément n'est pas un .media, vider #report__medias if (!nextElement || !nextElement.classList.contains('media')) { reportMedias.innerHTML = ''; currentMediaId = null; } } } }); }, { root: null, rootMargin: `-${totalOffset}px 0px 0px 0px`, threshold: 0 }); // Observer toutes les sections sections.forEach(section => { sectionObserver.observe(section); }); }