home double colonne vitesse différente, page projet empilement des images

This commit is contained in:
2026-08-15 21:55:05 +02:00
parent 9f9a2501ee
commit edec95abbf
6 changed files with 772 additions and 2 deletions
@@ -5,6 +5,11 @@ import "splitting/dist/splitting.css";
import "splitting/dist/splitting-cells.css";
import Splitting from "splitting";
import { hyphenateSync } from "hyphen/fr";
import { gsap, Power1 } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import Lenis from 'lenis';
gsap.registerPlugin(ScrollTrigger);
// import Content from './vuejs/Content.vue'
@@ -44,6 +49,8 @@ import { hyphenateSync } from "hyphen/fr";
console.log('LeShedTheme init()')
initTitles();
initBurgerMenu();
initSmoothScroll();
initColumnScrollSync();
// initVues()
}
@@ -219,6 +226,84 @@ import { hyphenateSync } from "hyphen/fr";
})
}
/**
* Scroll lissé (Lenis) sur le conteneur `main` — et non la fenêtre, car
* c'est `main[role="main"]` qui porte l'overflow. Lenis est branché sur le
* ticker GSAP (une seule boucle RAF) et pousse ses updates à ScrollTrigger
* pour que la synchro des colonnes reste calée sur le scroll lissé.
*/
function initSmoothScroll() {
const scroller = document.querySelector('main[role="main"]');
const content = scroller && scroller.querySelector('.layout-content');
if (!scroller || !content) return;
const lenis = new Lenis({
wrapper: scroller, // conteneur scrollable custom
content: content, // contenu réel (le 1er enfant est l'ancre skip-link)
duration: 1.2,
smoothWheel: true,
});
lenis.on('scroll', ScrollTrigger.update);
gsap.ticker.add((time) => lenis.raf(time * 1000));
gsap.ticker.lagSmoothing(0);
}
/**
* Synchronise le scroll des deux colonnes du layout 50/50 : la colonne la
* plus courte est ralentie (translateY décroissant) pour que son bas soit
* atteint en même temps que celui de la colonne la plus haute, qui défile
* nativement (flux normal, pas de pin). Le scroll réel a lieu dans
* `main[role="main"]`, pas dans la fenêtre : GSAP utilise donc ce conteneur
* comme `scroller`.
*/
function initColumnScrollSync() {
console.log('initColumnScrollSync');
const scroller = document.querySelector('main[role="main"]');
const firstColumn = document.querySelector('.layout--twocol-section--50-50 .layout__region--first');
const secondColumn = document.querySelector('.layout--twocol-section--50-50 .layout__region--second');
console.log(scroller, firstColumn, secondColumn);
if (!scroller || !firstColumn || !secondColumn) return;
const mm = gsap.matchMedia();
console.log(mm);
mm.add('(min-width: 768px)', () => {
[firstColumn, secondColumn].forEach((column) => {
gsap.to(column, {
// Distance à parcourir pour que le bas de la colonne arrive en
// bas du viewport en même temps que la colonne la plus haute.
y: () => {
const visibleHeight = scroller.clientHeight;
const scrollRange = scroller.scrollHeight - scroller.clientHeight;
const columnRevealRange = Math.max(0, column.offsetHeight - visibleHeight);
return scrollRange - columnRevealRange;
},
ease: 'none',
scrollTrigger: {
scroller: scroller,
start: 0,
end: 'max', // scroll maximum du conteneur (robuste au resize)
// Valeur numérique = easing : l'animation met ~0.6s à rattraper la
// position de scroll au lieu de la suivre au pixel près → lisse les
// à-coups de la colonne lente. Monter la valeur = plus d'inertie.
scrub: true,
invalidateOnRefresh: true,
},
});
});
// Nettoyage automatique des tweens/triggers quand on repasse sous 768px.
return () => {};
});
// Les images chargent après coup et changent la hauteur des colonnes.
window.addEventListener('load', () => ScrollTrigger.refresh());
}
// function initVues(){
// console.log('initVues');