home double colonne vitesse différente, page projet empilement des images
This commit is contained in:
@@ -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');
|
||||
|
||||
|
||||
@@ -210,6 +210,10 @@ header[role="banner"]{
|
||||
|
||||
div.layout.layout--twocol-section--50-50{
|
||||
flex-wrap: nowrap;
|
||||
// Sans ça, `align-items: stretch` (défaut flex) étire les deux colonnes à la
|
||||
// même hauteur : la synchro de scroll ne verrait aucune différence de hauteur.
|
||||
// flex-start = chaque colonne prend la hauteur de son contenu.
|
||||
align-items: flex-start;
|
||||
|
||||
column-gap: $colsgap;
|
||||
div.layout__region{
|
||||
@@ -298,10 +302,27 @@ article.node-type-projet.view-mode-full{
|
||||
}
|
||||
}
|
||||
&.right{
|
||||
// Étire .field_images sur toute la hauteur de la colonne (= colonne la
|
||||
// plus haute, via le stretch de .cols) : le contexte de collage sticky
|
||||
// couvre alors tout le scroll, donc les cartes restent figées tant que
|
||||
// la colonne gauche défile encore (cf. point 6).
|
||||
display: flex;
|
||||
.field_images{
|
||||
flex: 1;
|
||||
}
|
||||
img{
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
// Empilement "cartes" : chaque image se fige au même endroit (haut) et
|
||||
// est recouverte par la suivante (l'ordre du DOM fait que la dernière
|
||||
// passe au-dessus). La 1re image ne bouge pas. Desktop uniquement.
|
||||
@media (min-width: 768px){
|
||||
.field_images .field-item{
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user