Files
drupal-leshed/web/themes/custom/leshed/assets/js/main.js
T

318 lines
11 KiB
JavaScript

// import { createApp } from 'vue'
import '../scss/main.scss'
import "splitting/dist/splitting.css";
import "splitting/dist/splitting-cells.css";
import { gsap, Power1 } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import Lenis from 'lenis';
import 'fslightbox';
gsap.registerPlugin(ScrollTrigger);
// import Content from './vuejs/Content.vue'
// import REST from './api/rest-axios'
// import LocomotiveScroll from 'locomotive-scroll';
// const scroll = new LocomotiveScroll();
// /**
// * @file
// * https://www.drupal.org/docs/drupal-apis/javascript-api/javascript-api-overview
// */
// (function (Drupal) {
// 'use strict';
// Drupal.behaviors.leshed = {
// attach: function (context, settings) {
// console.log('It works!');
// }
// };
// } (Drupal));
(function (Drupal, drupalSettings) {
const LeShedTheme = function () {
const _is_front = drupalSettings.path.isFront
console.log('drupalSettings', drupalSettings)
const scroller = document.querySelector('main[role="main"]');
const gsap_mm = gsap.matchMedia();
console.log('gsap_mm', gsap_mm);
// ___ _ _
// |_ _|_ _ (_) |_
// | || ' \| | _|
// |___|_||_|_|\__|
function init () {
console.log('LeShedTheme init()');
console.log('deploy test to delete');
initBurgerMenu();
initSmoothScroll();
initHomeColumnScrollSync();
initFsLightbox();
initPageFull();
// initVues()
}
function onWindowResize() {
_cardsForms.forEach(({card, forme, formepos}) => {
// console.log(`card`, card, 'forme', forme, 'formepos', formepos);
forme.style.top = `${card.offsetTop + formepos.top}px`;
forme.style.left = `${card.offsetLeft + formepos.left}px`;
})
}
function initBurgerMenu() {
let header_right = document.getElementById('burger-btn');
header_right.parentElement.addEventListener('click', function(e){
// console.log('click header_right', this);
this.firstElementChild.toggleAttribute('opened');
this.firstElementChild.nextElementSibling.toggleAttribute('opened');
})
}
/**
* 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 initHomeColumnScrollSync() {
console.log('initHomeColumnScrollSync');
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;
gsap_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 initPageFull(){
console.log('initPageFull');
// PAGE TITLE SCALING DOWN
const pageHeader = document.querySelector('article.node-type-projet.view-mode-full .cols>.col.left>header, article.node-type-evenement.view-mode-full>.cols>.col.left>header');
console.log('pageHeader', pageHeader);
if (pageHeader) {
// needs requestAnimationFrame to get the real titleHeight after splitting
requestAnimationFrame(() => {
requestAnimationFrame(() => {
const initheaderH = pageHeader.offsetHeight;
// console.log(`initheaderH: ${initheaderH}`);
const headerScaleTarget = 0.4;
const end = initheaderH - initheaderH * headerScaleTarget;
// console.log(`end: ${end}`);
gsap_mm.add('(min-width: 768px)', () => {
gsap.to(pageHeader, {
scale: headerScaleTarget,
y: end,
ease: 'none',
scrollTrigger: {
scroller: scroller,
start: 0,
end: end, // scroll maximum du conteneur (robuste au resize)
scrub: true,
invalidateOnRefresh: true,
},
});
})
});
});
}
// BLOCAGE DE LA COLONNE GAUCHE
// La gauche défile normalement puis se fige (pin) quand le bas de son
// contenu atteint le bas du viewport, pendant que la colonne droite (plus
// haute) continue. Piloté en GSAP plutôt qu'en `position: sticky` (qui ne
// prend pas dans ce contexte scroller custom + GSAP). `.cols` est en
// `align-items: flex-start`, donc .col.left a bien la hauteur de son
// contenu (sinon `bottom bottom` tomberait à la fin du scroll).
const cols = document.querySelector('article.node-type-projet.view-mode-full .cols');
const leftColumn = cols && cols.querySelector('.col.left');
if (cols && leftColumn) {
// Doit rester identique au scale-down du titre (voir plus haut).
const titleScaleTarget = 0.4;
// Seuil de scroll (px) après lequel la colonne gauche se fige :
// - gauche plus haute que le viewport : quand son bas atteint le bas ;
// - gauche plus courte : quand le scale-down du titre est terminé, pour
// qu'elle remonte AVEC le titre puis s'arrête (sans disparaître en haut).
const leftFreezeStart = () => {
const overflow = leftColumn.offsetHeight - scroller.clientHeight;
if (overflow > 0) return overflow;
return initheaderH ? initheaderH.offsetHeight * (1 - titleScaleTarget) : 0;
};
gsap_mm.add('(min-width: 768px)', () => {
// On NE pin PAS (le pin sort l'élément du flux et casse le flex .cols).
// On translate la colonne : défilement natif jusqu'à `leftFreezeStart`,
// puis contre-translation 1:1 du scroll → elle se fige, tout en restant
// un flex-item normal (la colonne droite garde sa place).
gsap.fromTo(leftColumn,
{ y: 0 },
{
y: () => (scroller.scrollHeight - scroller.clientHeight) - leftFreezeStart(),
ease: 'none',
scrollTrigger: {
scroller: scroller,
start: leftFreezeStart, // px de scroll où la gauche se fige
end: 'max',
scrub: true,
invalidateOnRefresh: true,
},
}
);
});
}
}
function initFsLightbox(){
console.log('initFsLightbox');
// console.log(window.FsLightbox);
let imgs_links = document.querySelectorAll('article.node-type-projet.view-mode-full .field_images.multiple .field-item a');
for (const link of imgs_links) {
link.setAttribute("data-fslightbox", "gallery")
}
// window.FsLightbox
window.refreshFsLightbox();
}
// function initVues(){
// console.log('initVues');
// // initVueContent();
// }
// function initVueContent(){
// createApp(Content).mount('main[role="main"]');
// // processEtapeLinks();
// }
// function onClickEtapeLink(e){
// e.preventDefault();
// let a = e.currentTarget;
// let nid = a.dataset.nodeNid;
// console.log(nid);
// getNodeData(nid);
// return null;
// }
// function processEtapeLinks(){
// let etape_link_fields = document.querySelectorAll('#etapes-liste div.views-field-title');
// etape_link_fields.forEach((field, index) => {
// let nid = null;
// let classList = field.classList;
// classList.forEach((classe) => {
// let reg = /data-node-(\d+)/;
// let result = classe.match(reg);
// if (result) {
// nid = result[1];
// console.log(nid);
// }
// })
// if (nid) {
// let a = field.querySelector('a');
// a.setAttribute('data-node-nid', nid);
// a.addEventListener('click', onClickEtapeLink);
// }
// })
// }
// function getNodeData(nid){
// const params = {
// }
// REST.get(`/node/${nid}?_format=json`, params)
// .then((data) => {
// console.log('user REST getUser data', data)
// })
// .catch(error => {
// console.warn('Issue with getNodedata', error)
// Promise.reject(error)
// })
// }
init()
} // end LeShedTheme()
LeShedTheme()
})(Drupal, drupalSettings)