custom masonry on page archhives projets
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
+6
-6
File diff suppressed because one or more lines are too long
@@ -69,6 +69,7 @@ gsap.registerPlugin(ScrollTrigger);
|
||||
initHomeColumnScrollSync();
|
||||
initFsLightbox();
|
||||
initPageFull();
|
||||
initMasonry();
|
||||
// initVues()
|
||||
|
||||
// titles.js traite les titres hors écran par lots, après la première
|
||||
@@ -351,6 +352,102 @@ gsap.registerPlugin(ScrollTrigger);
|
||||
window.refreshFsLightbox();
|
||||
}
|
||||
|
||||
// __ __ _ _ _
|
||||
// | \/ | ___ _ __ ___| |__ ___ _ __ ___ ___| |_| |
|
||||
// | |\/| |/ _ \| '_ \ / __| '_ \ / _ \ '__/ _ \/ __| __| |
|
||||
// | | | | (_) | | | | (__| | | | __/ | | __/\__ \ |_|_|
|
||||
// |_| |_|\___/|_| |_|\___|_| |_|\___|_| \___||___/\__(_)
|
||||
//
|
||||
// Masonry maison pour la grille des projets (.view-content de
|
||||
// views-archives-projets) : CSS Grid + grid-auto-rows fin + span N par
|
||||
// item. Pas de librairie ; l'ordre gauche→droite de la vue est préservé
|
||||
// (auto-placement sparse, dense inutile : les trous résiduels font moins
|
||||
// d'une rangée virtuelle). Sans JS (ou avant le premier pack), la grille
|
||||
// CSS actuelle reste le fallback : des trous, mais rien de cassé.
|
||||
function initMasonry(){
|
||||
const containers = document.querySelectorAll('.views-archives-projets .view-content');
|
||||
if (!containers.length) return;
|
||||
|
||||
// Granularité d'une rangée virtuelle : assez fine pour que les trous
|
||||
// résiduels (≤ BASE) soient invisibles, assez grosse pour des spans
|
||||
// raisonnables.
|
||||
const BASE = 8; // px
|
||||
|
||||
const pack = (container) => {
|
||||
const items = container.querySelectorAll(':scope > .views-row');
|
||||
if (!items.length) return;
|
||||
// Gouttière verticale : row-gap de la grille (0 ici — la gouttière
|
||||
// vit dans le margin-bottom des items) ; le pas de quantification
|
||||
// du span est donc BASE et non BASE + gouttière.
|
||||
const rowGap = parseFloat(getComputedStyle(container).rowGap) || 0;
|
||||
|
||||
// Dense : sans lui, le placement sparse ne recule jamais — un item
|
||||
// haut qui ne rentre pas dans un trou le saute, et les suivants ne
|
||||
// peuvent plus y revenir (trous visibles sous la première rangée).
|
||||
// Dense renvoie chaque item au premier trou où il tient, quitte à
|
||||
// inverser localement l'ordre visuel — comportement standard d'un
|
||||
// masonry.
|
||||
container.style.gridAutoFlow = 'row dense';
|
||||
|
||||
// Les images portent width/height (ratio réservé au layout) et
|
||||
// align-items: start (SCSS) garantit qu'un item garde sa hauteur de
|
||||
// contenu, spans appliqués ou non. On mesure donc SANS reset des
|
||||
// spans : pas de re-layout complet de la grille à chaque pack —
|
||||
// chaque pack se résume à lire N hauteurs et n'écrire que les spans
|
||||
// qui changent (sinon chaque image chargée pendant le scroll
|
||||
// déclenchait deux layouts forcés des 110 items, d'où le lag).
|
||||
items.forEach((item) => {
|
||||
const cs = getComputedStyle(item);
|
||||
const h = item.offsetHeight
|
||||
+ (parseFloat(cs.marginTop) || 0)
|
||||
+ (parseFloat(cs.marginBottom) || 0);
|
||||
const span = Math.max(1, Math.ceil((h + rowGap) / (BASE + rowGap)));
|
||||
if (item._masonrySpan !== span) {
|
||||
item._masonrySpan = span;
|
||||
item.style.gridRowEnd = `span ${span}`;
|
||||
}
|
||||
});
|
||||
container.style.gridAutoRows = `${BASE}px`;
|
||||
};
|
||||
|
||||
const schedule = (container) => {
|
||||
// Une seule passe par frame, même si dix images lazy arrivent coup
|
||||
// sur coup.
|
||||
if (container._masonryRaf) return;
|
||||
container._masonryRaf = requestAnimationFrame(() => {
|
||||
container._masonryRaf = null;
|
||||
pack(container);
|
||||
});
|
||||
};
|
||||
|
||||
containers.forEach((container) => {
|
||||
// Toute variation de largeur (seuil mobile : 4→2 colonnes, zoom,
|
||||
// rotation) repasse par un pack complet.
|
||||
new ResizeObserver(() => schedule(container)).observe(container);
|
||||
|
||||
// Le contenu d'une carte peut changer de hauteur APRÈS un pack :
|
||||
// troncature des h2 par titles.js (data-overflow-lines), chargement
|
||||
// des images, polices… Or la views-row, étirée sur son span, ne
|
||||
// change PAS de taille quand son contenu rétrécit : le span reste
|
||||
// surdimensionné (l'espace mort dans la row) et aucun déclencheur
|
||||
// extérieur ne le voit. On observe donc directement les articles.
|
||||
const contentRO = new ResizeObserver(() => schedule(container));
|
||||
container.querySelectorAll(':scope > .views-row > article').forEach((a) => contentRO.observe(a));
|
||||
|
||||
// Images lazy : chaque chargement change la hauteur d'un item.
|
||||
container.addEventListener('load', () => schedule(container), true);
|
||||
|
||||
// La police variable (Epilogue) charge après le layout initial, et
|
||||
// titles.js retouche les h2 par lots : les deux changent des hauteurs.
|
||||
if (document.fonts && document.fonts.ready) {
|
||||
document.fonts.ready.then(() => schedule(container));
|
||||
}
|
||||
document.addEventListener('leshed:titles-ready', () => schedule(container), { once: true });
|
||||
|
||||
schedule(container);
|
||||
});
|
||||
}
|
||||
|
||||
// function initVues(){
|
||||
// console.log('initVues');
|
||||
|
||||
|
||||
@@ -42,7 +42,22 @@ div.views-archives-projets{
|
||||
// seuil mobile. Une dernière rangée incomplète ne s'étire pas.
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: $colsgap;
|
||||
// Gouttière verticale portée par les items (margin-bottom), pas par le
|
||||
// row-gap : avec le masonry JS, chaque rangée virtuelle de 8px ajouterait
|
||||
// 32px de pas de quantification — un item peut « dépasser » de presque
|
||||
// 40px dans son span. Avec row-gap: 0, l'arrondi résiduel tombe sous
|
||||
// 8px. Sans JS, la marge produit la même gouttière que l'ancien gap.
|
||||
column-gap: $colsgap;
|
||||
row-gap: 0;
|
||||
align-items: start;
|
||||
>.views-row{
|
||||
margin-bottom: $colsgap;
|
||||
// Le whitespace du template crée une boîte de ligne anonyme (+16px)
|
||||
// sous l'article. Un conteneur flex ignore les nœuds texte vides :
|
||||
// la row fait exactement la hauteur de l'article.
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@media #{$mobile}{
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user