Files
drupal-leshed/SCROLL_FIX_REPORT.md

7.8 KiB

Rapport : corrections scroll colonnes gauche/droite et scale-down du titre

Contexte

Sur les pages full des projets et evenements, deux animations GSAP coexistent :

  1. Scale-down du titre : le header (titre) retrecit de scale 1.0 -> 0.4 pendant le scroll, avec une translation y qui compense pour garder le bord visuel fixe.
  2. Gel de la colonne gauche : la colonne gauche defile naturellement puis se fige (contre-translation 1:1 du scroll) pendant que la colonne droite (images en position: sticky) continue.

Les images de la colonne droite sont en position: sticky; top: 0 et s'empilent les unes sur les autres.

Problemes identifies

1. node/573 — image remonte en fin de scroll (FIXE)

Cause : le :after de .field_images avait height: 100vh statique. Quand la colonne gauche est tres haute (texte long), le contexte sticky s'epuisait avant la fin du scroll, et l'image remontait.

Fix :

  • CSS : height: var(--sticky-extent, 100vh) sur le :after (fallback 100vh si pas de JS).
  • JS : adjustStickyContext() calcule scrollRange - imgHeight + clientHeight et pose la valeur en CSS custom property --sticky-extent sur .field_images. Recalcule au load de chaque image.

Status : fonctionne.

2. node/576 — ecart entre le titre et le contenu (FIXE puis REGRESSIF)

Cause : initHeaderH etait mesure une fois, de maniere synchrone, avant les requestAnimationFrame. Si la police Epilogue ou Splitting changeait la hauteur du header apres cette mesure, end et y etaient faux, creant un ecart visuel entre le bas du header et le contenu.

Fix applique :

  • y et end transformes en fonctions qui mesurent pageHeader.offsetHeight au moment ou GSAP en a besoin.
  • initHeaderH supprime, remplace par pageHeader.offsetHeight partout (y compris dans leftFreezeStart).

Status : l'ecart est corrige, mais le fix a introduit un nouveau probleme (voir point 4).

3. node/549 et 570 — colonne gauche redescend en fin de scroll (REGRESSIF puis REFIXE)

Cause : leftFreezeStart utilisait Math.max(overflow, titleEnd). Sur 549/570, titleEnd (proportionnel a la hauteur du titre a 12em) peut depasser overflow (hauteur naturelle de la colonne - viewport), ce qui decale le freeze trop loin et la colonne redescend.

Fix applique : retour a la logique d'origine pour leftFreezeStart :

const overflow = leftColumn.offsetHeight - scroller.clientHeight;
if (overflow > 0) return overflow;
return pageHeader ? pageHeader.offsetHeight * (1 - headerScaleTarget) : 0;

Status : fonctionne pour 549/570, mais casse 576 (voir point 4).

4. node/576 — colonne se fige trop tot, titre diminue en descendant (REGRESSIF)

Cause : ensureScrollForTitle agrandit le spacer de la colonne gauche pour garantir assez de scroll pour le scale-down du titre. Le spacer rend overflow > 0, donc leftFreezeStart retourne overflow. Mais overflow peut etre inferieur a titleEnd (le spacer donne juste assez de scroll pour end, pas assez pour que overflow = end). La colonne se fige avant la fin du scale-down, le y pousse le titre vers le bas.

Tentative de fix avec Math.max(overflow, titleEnd) : corrige 576 mais casse 549/570 (voir point 3).

Fix actuel : ensureScrollForTitle est appele avant la creation des ScrollTriggers. La formule du spacer est spacerAdd = end + viewport - colHeight, ce qui garantit que overflow = end = titleEnd apres l'agrandissement. La logique d'origine de leftFreezeStart (if overflow > 0 return overflow) retourne alors une valeur >= titleEnd.

Status : a tester. Le succes depend du timing : si le spacer est bien en place avant le calcul de leftFreezeStart, tout est coherent.

5. node/586 — pas d'image, scroll insuffisant pour le scale-down (NON FIXE)

Cause : sans image, la colonne droite est vide. Le contenu de la colonne gauche est court. Le scroll naturel est insuffisant pour que le titre termine son scale-down.

Fix tente : ensureScrollForTitle agrandit le spacer avec la formule spacerAdd = end + viewport - colHeight. Le scroll resultant devrait etre exactement end.

Probleme : le spacer etait calcule avec l'ancienne formule end - scrollRange qui ne tenait pas compte du viewport. La nouvelle formule corrige le calcul, mais le lenis.resize() au window.load est necessaire pour que Lenis recalcule sa plage de scroll apres le chargement de la police.

Status : partiellement corrige. Le scroll est cree mais peut etre insuffisant si Lenis ne refresh pas correctement. Le lenis.resize() au load a ete ajoute dans initSmoothScroll.

6. node/545 et 583 — pas d'image, texte court, pas de scroll (OK)

Ces pages n'ont pas d'image et le contenu est court. Il n'y a pas de scroll, le titre ne diminue pas. C'est le comportement attendu et accepte.

Fichiers modifies

assets/js/main.js

  1. initSmoothScroll() :

    • Ajout de lenis.resize() + ScrollTrigger.refresh() au window.load
    • return lenis pour permettre un acces futur
  2. initPageFull() :

    • cols, leftColumn, rightColumn, spacer, spacerBaseHeight declares en haut de la fonction
    • adjustStickyContext() defini avant ensureScrollForTitle, calcule --sticky-extent base sur scrollRange - imgHeight + clientHeight
    • ensureScrollForTitle() : agrandit le spacer si scrollRange < end. Formule : spacerAdd = end + viewport - colHeight. Appele avant le gsap.to() et au window.load.
    • y et end du ScrollTrigger du titre : transformes en fonctions () => pageHeader.offsetHeight * (1 - headerScaleTarget)
    • initHeaderH supprime
    • leftFreezeStart : logique d'origine (overflow si > 0, sinon titleEnd), mais avec pageHeader.offsetHeight au lieu de initHeaderH

assets/scss/main.scss

  1. :after de .field_images : height: 100vh remplace par height: var(--sticky-extent, 100vh)

assets/js/titles.js

  • Ajout de measureOverflow() : mesure le debordement du titre sous l'image et pose data-overflow-lines sur le h2. Utilise au scroll pour les pages liste (home, archives, projets lies).
  • Appele dans initTitles() apres le traitement Splitting de chaque titre.

Points non resolus

  1. 586 : le scroll cree par le spacer peut etre insuffisant si Lenis ne refresh pas correctement. Le lenis.resize() au load devrait aider mais n'a pas ete confirme.
  2. 576 : depend du timing de ensureScrollForTitle par rapport aux ScrollTriggers. Si le spacer n'est pas en place avant le calcul de leftFreezeStart, la colonne se fige trop tot.
  3. Interaction ensureScrollForTitle / adjustStickyContext : les deux se recalculent mutuellement mais peuvent entrer en conflit si les hauteurs changent apres le chargement.
  4. 549/570 : la logique d'origine de leftFreezeStart fonctionne, mais toute modification qui change overflow (comme le spacer) peut casser le comportement.

Approches envisagees mais abandonnees

  1. Math.max(overflow, titleEnd) dans leftFreezeStart : corrige 576/586 mais casse 549/570.
  2. format: 'iife' dans Vite : incompatible avec multiple entry points.
  3. format: 'es' dans Vite : ne change pas le output (deja en format classique).
  4. type: module dans leshed.libraries.yml : pour resoudre la redeclaration const ct entre titles.js et main.js.applique mais non teste en prod.

Tests a faire

  • 549 : colonne gauche ne redescend pas en fin de scroll
  • 570 : colonne gauche ne redescend pas en fin de scroll
  • 576 : titre diminue sans descendre, contenu colle au header
  • 573 : image ne remonte pas en fin de scroll
  • 586 : titre peut terminer son scale-down
  • 545 : pas de scroll, titre ne diminue pas (OK)
  • 583 : pas de scroll, titre ne diminue pas (OK)
  • Pages liste (home, archives) : data-overflow-lines fonctionne