décors ponctuels (oiseaux, fleurs) sur home, pages internes et footer
BIN
web/themes/erabletheme/assets/drawings/bird-1.png
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
web/themes/erabletheme/assets/drawings/bird-2.png
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
web/themes/erabletheme/assets/drawings/bird-3.png
Normal file
|
After Width: | Height: | Size: 197 KiB |
BIN
web/themes/erabletheme/assets/drawings/flower-1.png
Normal file
|
After Width: | Height: | Size: 317 KiB |
BIN
web/themes/erabletheme/assets/drawings/flower-2.png
Normal file
|
After Width: | Height: | Size: 173 KiB |
BIN
web/themes/erabletheme/assets/drawings/flower-3.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
web/themes/erabletheme/assets/new-bg-shapes/home-diapo-left.png
Normal file
|
After Width: | Height: | Size: 338 KiB |
BIN
web/themes/erabletheme/assets/new-bg-shapes/home-diapo-right.png
Normal file
|
After Width: | Height: | Size: 295 KiB |
@@ -79,7 +79,7 @@ body .layout-container main {
|
||||
}
|
||||
}
|
||||
body .layout-container footer {
|
||||
z-index: 0;
|
||||
z-index: 3;
|
||||
}
|
||||
body .layout-container #background {
|
||||
z-index: -1;
|
||||
@@ -399,6 +399,7 @@ footer.fluo_links p {
|
||||
margin-top: 50px;
|
||||
padding: 1rem 3vw;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
@media (min-width: 1080px) {
|
||||
.layout-container > footer {
|
||||
@@ -407,6 +408,33 @@ footer.fluo_links p {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
.layout-container > footer > .footer-decor {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
width: 18vw;
|
||||
height: auto;
|
||||
transform: translateY(15%);
|
||||
pointer-events: none;
|
||||
}
|
||||
@media (min-width: 1080px) {
|
||||
.layout-container > footer > .footer-decor {
|
||||
width: 8vw;
|
||||
}
|
||||
}
|
||||
.layout-container > footer > .footer-decor-bird {
|
||||
left: 0;
|
||||
}
|
||||
.layout-container > footer > .footer-decor-flower {
|
||||
right: 0;
|
||||
}
|
||||
@media (min-width: 1080px) {
|
||||
.layout-container > footer > .footer-decor-bird {
|
||||
left: -4vw;
|
||||
}
|
||||
.layout-container > footer > .footer-decor-flower {
|
||||
right: -4vw;
|
||||
}
|
||||
}
|
||||
.layout-container > footer #footer_middle {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -1182,11 +1210,12 @@ main.main-login .login > div > div:not(.hidden) form .button:hover, main.main-lo
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 5vh;
|
||||
}
|
||||
@media (min-width: 1080px) {
|
||||
.map-projets-section {
|
||||
margin-top: 10vh;
|
||||
margin-bottom: 10vh;
|
||||
margin-bottom: 4vh;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1441,6 +1470,11 @@ main:not(:has(#block-erabletheme-leprogramme-2)):not(:has(#block-erabletheme-vie
|
||||
border-bottom: 7px solid #33ffc4;
|
||||
position: relative;
|
||||
}
|
||||
@media (min-width: 1080px) {
|
||||
.layout-content .fullpage {
|
||||
margin-bottom: 4vh;
|
||||
}
|
||||
}
|
||||
.layout-content .fullpage h2 {
|
||||
margin-top: 5vh !important;
|
||||
margin-bottom: 7vh;
|
||||
@@ -1492,7 +1526,7 @@ main:not(:has(#block-erabletheme-leprogramme-2)):not(:has(#block-erabletheme-vie
|
||||
color: #4a4a49;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.layout-content .fullpage .fullpage_content p:first-of-type {
|
||||
.layout-content .fullpage .fullpage_content :is(p, h1, h2, h3, h4, h5, h6):first-child {
|
||||
margin-top: 7vh;
|
||||
}
|
||||
.layout-content .fullpage .fullpage_content .liens_fixed > div > div, .layout-content .fullpage .fullpage_content .file_fixed > div > div,
|
||||
|
||||
@@ -440,14 +440,14 @@
|
||||
}
|
||||
|
||||
//
|
||||
// Décors mid-left : 0, 1 ou 2 PNG empilés au pied gauche extérieur
|
||||
// de chaque .fullpage interne (pages non-index). Tailles et gaps
|
||||
// proportionnels à colH, bornés min/max. On n'affiche un motif
|
||||
// que si la pile tient sous 50% colH (les deux) ou 30% (un seul),
|
||||
// sinon la forme déborderait dans le contenu en haut.
|
||||
// Position absolue calculée en px dans .layout-container ;
|
||||
// Décors mid-left (col-decor) : PNG empilés au pied gauche
|
||||
// extérieur d'une colonne. Position absolue dans .layout-container,
|
||||
// bord droit du PNG calé sur bord gauche colonne via
|
||||
// translateX(-100%). Parallax custom plus bas (sans Rellax).
|
||||
// translateX(-100%). Tailles + gaps proportionnels à colH (bornés
|
||||
// min/max). Affichage conditionnel : seuils 50%/30% colH.
|
||||
// - .fullpage:not(.large-container) : pile de 2 (mid-left-1+2).
|
||||
// - .map-projets (home) : 1 seul (mid-left-1) via singleOnly.
|
||||
// Parallax custom (sans Rellax) plus bas.
|
||||
// Visibilité responsive : _background.scss.
|
||||
//
|
||||
const layoutContainer = document.querySelector('.layout-container');
|
||||
@@ -455,6 +455,17 @@
|
||||
|
||||
function clamp(min, val, max) { return Math.max(min, Math.min(val, max)); }
|
||||
|
||||
// Coefficient appliqué aux décors home (oiseaux + fleurs) hors
|
||||
// desktop : on grossit les images pour qu'elles restent lisibles
|
||||
// sur écrans étroits (le viewport rétrécit mais les éléments
|
||||
// décoratifs en vw deviennent visuellement trop petits).
|
||||
function decorWidthScale() {
|
||||
const w = window.innerWidth;
|
||||
if (w < 760) return 1.8; // mobile
|
||||
if (w < 1080) return 1.5; // tablette
|
||||
return 1; // desktop
|
||||
}
|
||||
|
||||
function colDecorMetrics(colH) {
|
||||
return {
|
||||
gapBottom: clamp(120, 0.08 * colH, 320),
|
||||
@@ -478,6 +489,7 @@
|
||||
if (!layoutContainer) return;
|
||||
layoutContainer.querySelectorAll('.col-decor').forEach(n => n.remove());
|
||||
colDecorState.length = 0;
|
||||
// Colonnes .fullpage internes : pile de 2 PNG (mid-left-1 et -2).
|
||||
document.querySelectorAll('.fullpage:not(.large-container)').forEach(col => {
|
||||
const img1 = createColDecorImg(1);
|
||||
const img2 = createColDecorImg(2);
|
||||
@@ -485,6 +497,14 @@
|
||||
layoutContainer.appendChild(img2);
|
||||
colDecorState.push({ img1, img2, col });
|
||||
});
|
||||
// Bloc .map-projets (home) : un seul PNG via singleOnly.
|
||||
document.querySelectorAll('.map-projets').forEach(col => {
|
||||
const img1 = createColDecorImg(1);
|
||||
const img2 = createColDecorImg(2);
|
||||
layoutContainer.appendChild(img1);
|
||||
layoutContainer.appendChild(img2);
|
||||
colDecorState.push({ img1, img2, col, singleOnly: true });
|
||||
});
|
||||
positionColDecor();
|
||||
}
|
||||
|
||||
@@ -494,36 +514,44 @@
|
||||
const lcTopAbs = lcRect.top + window.scrollY;
|
||||
const lcLeftAbs = lcRect.left + window.scrollX;
|
||||
for (const entry of colDecorState) {
|
||||
const { img1, img2, col } = entry;
|
||||
const { img1, img2, col, singleOnly } = entry;
|
||||
const r = col.getBoundingClientRect();
|
||||
const colH = r.height;
|
||||
const m = colDecorMetrics(colH);
|
||||
const stack2H = m.gapBottom + m.h2;
|
||||
const stack1H = stack2H + m.gapBetween + m.h1;
|
||||
const showBoth = stack1H <= colH * 0.5;
|
||||
const showOne = !showBoth && stack2H <= colH * 0.3;
|
||||
img1.style.display = showBoth ? 'block' : 'none';
|
||||
img2.style.display = (showBoth || showOne) ? 'block' : 'none';
|
||||
entry.visible1 = showBoth;
|
||||
entry.visible2 = showBoth || showOne;
|
||||
if (!showBoth && !showOne) continue;
|
||||
const stack1H = m.gapBottom + m.h2 + m.gapBetween + m.h1;
|
||||
// singleOnly : un seul motif (img1) si la place le permet
|
||||
// (= gapBottom + h1 sous 50% colH).
|
||||
const showBoth = !singleOnly && stack1H <= colH * 0.5;
|
||||
const showOne = !showBoth && (m.gapBottom + m.h2) <= colH * 0.3;
|
||||
const showSingle = singleOnly && (m.gapBottom + m.h1) <= colH * 0.5;
|
||||
entry.visible1 = showBoth || showSingle;
|
||||
entry.visible2 = !singleOnly && (showBoth || showOne);
|
||||
img1.style.display = entry.visible1 ? 'block' : 'none';
|
||||
img2.style.display = entry.visible2 ? 'block' : 'none';
|
||||
if (!entry.visible1 && !entry.visible2) continue;
|
||||
const colBottomAbs = r.bottom + window.scrollY;
|
||||
const leftRel = (r.left + window.scrollX) - lcLeftAbs;
|
||||
const top2 = colBottomAbs - m.gapBottom - m.h2;
|
||||
img2.style.top = (top2 - lcTopAbs) + 'px';
|
||||
img2.style.left = leftRel + 'px';
|
||||
img2.style.height = m.h2 + 'px';
|
||||
// Référence scroll : motif centré dans le viewport.
|
||||
entry.scrollRef2 = top2 + m.h2 / 2 - window.innerHeight / 2;
|
||||
if (showBoth) {
|
||||
const top1 = top2 - m.gapBetween - m.h1;
|
||||
if (singleOnly) {
|
||||
const top1 = colBottomAbs - m.gapBottom - m.h1;
|
||||
img1.style.top = (top1 - lcTopAbs) + 'px';
|
||||
img1.style.left = leftRel + 'px';
|
||||
img1.style.height = m.h1 + 'px';
|
||||
entry.scrollRef1 = top1 + m.h1 / 2 - window.innerHeight / 2;
|
||||
} else {
|
||||
const top2 = colBottomAbs - m.gapBottom - m.h2;
|
||||
img2.style.top = (top2 - lcTopAbs) + 'px';
|
||||
img2.style.left = leftRel + 'px';
|
||||
img2.style.height = m.h2 + 'px';
|
||||
entry.scrollRef2 = top2 + m.h2 / 2 - window.innerHeight / 2;
|
||||
if (showBoth) {
|
||||
const top1 = top2 - m.gapBetween - m.h1;
|
||||
img1.style.top = (top1 - lcTopAbs) + 'px';
|
||||
img1.style.left = leftRel + 'px';
|
||||
img1.style.height = m.h1 + 'px';
|
||||
entry.scrollRef1 = top1 + m.h1 / 2 - window.innerHeight / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Premier rendu du parallax après (re)position.
|
||||
applyColDecorParallax();
|
||||
}
|
||||
|
||||
@@ -559,6 +587,7 @@
|
||||
requestAnimationFrame(() => {
|
||||
colDecorRafPending = false;
|
||||
applyColDecorParallax();
|
||||
applyHomeDiapoParallax();
|
||||
});
|
||||
}
|
||||
if (!document.body.dataset.colDecorScrollWired) {
|
||||
@@ -566,6 +595,279 @@
|
||||
document.body.dataset.colDecorScrollWired = 'true';
|
||||
}
|
||||
|
||||
//
|
||||
// Décor aléatoire en haut à droite de chaque .fullpage. Tirage
|
||||
// parmi drawings/ (excluant flower-2 + bird-3 déjà au footer).
|
||||
// Bas de l'image overlap le haut de l'ancre (20%, sauf flower-3
|
||||
// à 40% car tige fine). Mobile : ancre = .page-header-outside.
|
||||
// Largeur par breakpoint.
|
||||
//
|
||||
const PAGE_DECOR_POOL = ['bird-1.png', 'bird-2.png', 'flower-1.png', 'flower-3.png'];
|
||||
const pageDecorState = [];
|
||||
|
||||
function pageDecorWidthVw() {
|
||||
const w = window.innerWidth;
|
||||
if (w < 1080) return 22; // mobile + tablette
|
||||
return 10; // desktop
|
||||
}
|
||||
|
||||
function setupPageDecor() {
|
||||
if (!layoutContainer) return;
|
||||
layoutContainer.querySelectorAll('.page-decor').forEach(n => n.remove());
|
||||
pageDecorState.length = 0;
|
||||
document.querySelectorAll('.fullpage').forEach(col => {
|
||||
// Exclure les .fullpage situées dans une sidebar (vue projets
|
||||
// imbriquée). On ne décore que les colonnes de contenu.
|
||||
if (col.closest('aside')) return;
|
||||
// Tirage déterministe par colonne : on stocke l'index sur le
|
||||
// dataset pour qu'un resize ne change pas l'image affichée.
|
||||
let idx = col.dataset.pageDecorIdx;
|
||||
if (idx == null) {
|
||||
idx = Math.floor(Math.random() * PAGE_DECOR_POOL.length);
|
||||
col.dataset.pageDecorIdx = String(idx);
|
||||
} else {
|
||||
idx = parseInt(idx, 10);
|
||||
}
|
||||
const img = document.createElement('img');
|
||||
img.className = 'page-decor';
|
||||
img.setAttribute('aria-hidden', 'true');
|
||||
img.setAttribute('alt', '');
|
||||
img.src = `/themes/erabletheme/assets/drawings/${PAGE_DECOR_POOL[idx]}`;
|
||||
img.style.cssText = 'position:absolute;width:auto;height:auto;pointer-events:none;z-index:0;';
|
||||
layoutContainer.appendChild(img);
|
||||
pageDecorState.push({ img, col });
|
||||
});
|
||||
positionPageDecor();
|
||||
}
|
||||
|
||||
function positionPageDecor() {
|
||||
if (!pageDecorState.length) return;
|
||||
const lcRect = layoutContainer.getBoundingClientRect();
|
||||
const lcTopAbs = lcRect.top + window.scrollY;
|
||||
const lcLeftAbs = lcRect.left + window.scrollX;
|
||||
const widthVw = pageDecorWidthVw();
|
||||
const widthPx = (widthVw / 100) * window.innerWidth;
|
||||
const isMobile = window.innerWidth < 760;
|
||||
for (const entry of pageDecorState) {
|
||||
const img = entry.img;
|
||||
if (!img.naturalWidth) {
|
||||
img.addEventListener('load', positionPageDecor, { once: true });
|
||||
continue;
|
||||
}
|
||||
const heightPx = widthPx * (img.naturalHeight / img.naturalWidth);
|
||||
// Mobile : ancre = .page-header-outside (titre/retour) si présent,
|
||||
// sinon la colonne elle-même.
|
||||
let anchorEl = entry.col;
|
||||
if (isMobile) {
|
||||
const layoutContent = entry.col.closest('.layout-content');
|
||||
const phOutside = layoutContent && layoutContent.querySelector('.page-header-outside');
|
||||
if (phOutside) anchorEl = phOutside;
|
||||
}
|
||||
const r = anchorEl.getBoundingClientRect();
|
||||
const anchorTopAbs = r.top + window.scrollY;
|
||||
const anchorRightAbs = r.right + window.scrollX;
|
||||
// flower-3 a une tige fine en haut → on la pousse plus dans
|
||||
// l'ancre (40%) pour qu'elle ne soit pas perdue dans la marge.
|
||||
const overlapRatio = img.src.includes('flower-3') ? 0.4 : 0.2;
|
||||
const top = anchorTopAbs + heightPx * overlapRatio - heightPx;
|
||||
const left = anchorRightAbs - widthPx * 0.7; // 30% dépasse à droite
|
||||
img.style.width = widthPx + 'px';
|
||||
img.style.top = (top - lcTopAbs) + 'px';
|
||||
img.style.left = (left - lcLeftAbs) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Décors home-diapo-{right,left} : ancrés aux bords droit/gauche
|
||||
// du viewport, leur haut dépassant au-dessus du haut des cards
|
||||
// du carousel home (overlapTop px). Parallax custom.
|
||||
//
|
||||
const HOME_DIAPO_CONFIGS = [
|
||||
{ side: 'right', file: 'home-diapo-right.png', widthVw: 30, overlapTop: 160 },
|
||||
{ side: 'left', file: 'home-diapo-left.png', widthVw: 28, overlapTop: 80 },
|
||||
];
|
||||
const homeDiapoState = [];
|
||||
|
||||
// Ancre cible : .slick-list (cards visibles), avec fallbacks.
|
||||
// .carousel_container inclut les dots → bas trop bas, à éviter.
|
||||
function getCarouselCardsBox() {
|
||||
return document.querySelector('.carousel_container .slick-list')
|
||||
|| document.querySelector('.carousel_container .slick-container')
|
||||
|| document.querySelector('.carousel_container');
|
||||
}
|
||||
|
||||
function setupHomeDiapoDecor() {
|
||||
if (!layoutContainer) return;
|
||||
layoutContainer.querySelectorAll('.home-diapo-decor').forEach(n => n.remove());
|
||||
homeDiapoState.length = 0;
|
||||
const cardsBox = getCarouselCardsBox();
|
||||
if (!cardsBox) return;
|
||||
for (const cfg of HOME_DIAPO_CONFIGS) {
|
||||
const img = document.createElement('img');
|
||||
img.className = `home-diapo-decor home-diapo-decor-${cfg.side}`;
|
||||
img.setAttribute('aria-hidden', 'true');
|
||||
img.setAttribute('alt', '');
|
||||
img.src = `/themes/erabletheme/assets/new-bg-shapes/${cfg.file}`;
|
||||
img.style.cssText = `position:absolute;${cfg.side}:0;width:auto;height:auto;pointer-events:none;z-index:0;`;
|
||||
layoutContainer.appendChild(img);
|
||||
homeDiapoState.push({ img, widthVw: cfg.widthVw, overlapTop: cfg.overlapTop, cardsBox });
|
||||
}
|
||||
positionHomeDiapoDecor();
|
||||
}
|
||||
|
||||
function positionHomeDiapoDecor() {
|
||||
if (!homeDiapoState.length) return;
|
||||
const lcRect = layoutContainer.getBoundingClientRect();
|
||||
const lcTopAbs = lcRect.top + window.scrollY;
|
||||
const scale = decorWidthScale();
|
||||
for (const entry of homeDiapoState) {
|
||||
const widthPx = (entry.widthVw / 100) * window.innerWidth * scale;
|
||||
const cRect = entry.cardsBox.getBoundingClientRect();
|
||||
const cardsTopAbs = cRect.top + window.scrollY;
|
||||
// Top = haut des cards - overlapTop : le haut de l'image
|
||||
// dépasse de cette valeur au-dessus du haut des cards.
|
||||
const top = cardsTopAbs - entry.overlapTop;
|
||||
entry.img.style.width = widthPx + 'px';
|
||||
entry.img.style.top = (top - lcTopAbs) + 'px';
|
||||
entry.scrollRef = top - window.innerHeight / 2;
|
||||
}
|
||||
applyHomeDiapoParallax();
|
||||
}
|
||||
|
||||
function applyHomeDiapoParallax() {
|
||||
if (!homeDiapoState.length) return;
|
||||
const scrollY = window.scrollY || document.body.scrollTop || 0;
|
||||
for (const { img, scrollRef } of homeDiapoState) {
|
||||
if (scrollRef == null) continue;
|
||||
const dY = clamp(-COL_DECOR_PARALLAX_AMP,
|
||||
-(scrollY - scrollRef) * COL_DECOR_PARALLAX_SPEED,
|
||||
COL_DECOR_PARALLAX_AMP);
|
||||
img.style.transform = `translate3d(0, ${dY}px, 0)`;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Décors home-above : flower-1 (gauche) et bird-1 (droite),
|
||||
// au-dessus des cards, leur bas débordant de `overlap` px sur le
|
||||
// haut des cards. offsetVw : décalage horizontal vers l'intérieur.
|
||||
// Pas de parallax.
|
||||
//
|
||||
const HOME_ABOVE_CONFIGS = [
|
||||
{ side: 'left', file: 'drawings/flower-1.png', widthVw: 15, overlap: 40, offsetVw: 8 },
|
||||
{ side: 'right', file: 'drawings/bird-1.png', widthVw: 15, overlap: 40, offsetVw: 4 },
|
||||
];
|
||||
const homeAboveState = [];
|
||||
|
||||
function setupHomeAboveDecor() {
|
||||
if (!layoutContainer) return;
|
||||
layoutContainer.querySelectorAll('.home-above-decor').forEach(n => n.remove());
|
||||
homeAboveState.length = 0;
|
||||
const cardsBox = getCarouselCardsBox();
|
||||
if (!cardsBox) return;
|
||||
for (const cfg of HOME_ABOVE_CONFIGS) {
|
||||
const img = document.createElement('img');
|
||||
img.className = `home-above-decor home-above-decor-${cfg.side}`;
|
||||
img.setAttribute('aria-hidden', 'true');
|
||||
img.setAttribute('alt', '');
|
||||
img.src = `/themes/erabletheme/assets/${cfg.file}`;
|
||||
img.style.cssText = `position:absolute;${cfg.side}:${cfg.offsetVw}vw;width:auto;height:auto;pointer-events:none;z-index:0;`;
|
||||
layoutContainer.appendChild(img);
|
||||
homeAboveState.push({ img, widthVw: cfg.widthVw, overlap: cfg.overlap, cardsBox });
|
||||
}
|
||||
positionHomeAboveDecor();
|
||||
}
|
||||
|
||||
function positionHomeAboveDecor() {
|
||||
if (!homeAboveState.length) return;
|
||||
const lcRect = layoutContainer.getBoundingClientRect();
|
||||
const lcTopAbs = lcRect.top + window.scrollY;
|
||||
const lcHeight = lcRect.height;
|
||||
const scale = decorWidthScale();
|
||||
for (const entry of homeAboveState) {
|
||||
const widthPx = (entry.widthVw / 100) * window.innerWidth * scale;
|
||||
const cRect = entry.cardsBox.getBoundingClientRect();
|
||||
const cardsTopAbs = cRect.top + window.scrollY;
|
||||
// Le BAS de l'image est à `overlap` px sous le haut des cards :
|
||||
// l'image est essentiellement au-dessus du carousel, débord en bas.
|
||||
const imgBottom = cardsTopAbs + entry.overlap;
|
||||
const bottomFromLcBottom = (lcTopAbs + lcHeight) - imgBottom;
|
||||
entry.img.style.width = widthPx + 'px';
|
||||
entry.img.style.bottom = bottomFromLcBottom + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Décors home-projet, ancrés au bloc .map-projets :
|
||||
// - bird-2 en haut à gauche, débordant à gauche du bloc en
|
||||
// desktop, ou collé à -3vw du viewport en mobile/tablette.
|
||||
// - flower-3 collée au bord droit du viewport (tronquée), centre
|
||||
// vertical à 1/3 depuis le haut du bloc.
|
||||
// Pas de parallax.
|
||||
//
|
||||
const HOME_PROJET_CONFIGS = [
|
||||
{ file: 'drawings/bird-2.png', widthVw: 15, anchor: 'block-left-top' },
|
||||
{ file: 'drawings/flower-3.png', widthVw: 7, anchor: 'viewport-right' },
|
||||
];
|
||||
const homeProjetState = [];
|
||||
|
||||
function setupHomeProjetDecor() {
|
||||
if (!layoutContainer) return;
|
||||
layoutContainer.querySelectorAll('.home-projet-decor').forEach(n => n.remove());
|
||||
homeProjetState.length = 0;
|
||||
const block = document.querySelector('.map-projets');
|
||||
if (!block) return;
|
||||
for (const cfg of HOME_PROJET_CONFIGS) {
|
||||
const img = document.createElement('img');
|
||||
img.className = `home-projet-decor home-projet-decor-${cfg.anchor}`;
|
||||
img.setAttribute('aria-hidden', 'true');
|
||||
img.setAttribute('alt', '');
|
||||
img.src = `/themes/erabletheme/assets/${cfg.file}`;
|
||||
img.style.cssText = 'position:absolute;width:auto;height:auto;pointer-events:none;z-index:0;';
|
||||
layoutContainer.appendChild(img);
|
||||
homeProjetState.push({ img, anchor: cfg.anchor, widthVw: cfg.widthVw, block });
|
||||
}
|
||||
positionHomeProjetDecor();
|
||||
}
|
||||
|
||||
function positionHomeProjetDecor() {
|
||||
if (!homeProjetState.length) return;
|
||||
const lcRect = layoutContainer.getBoundingClientRect();
|
||||
const lcTopAbs = lcRect.top + window.scrollY;
|
||||
const lcLeftAbs = lcRect.left + window.scrollX;
|
||||
const scale = decorWidthScale();
|
||||
for (const entry of homeProjetState) {
|
||||
const img = entry.img;
|
||||
if (!img.naturalWidth) {
|
||||
img.addEventListener('load', positionHomeProjetDecor, { once: true });
|
||||
continue;
|
||||
}
|
||||
const widthPx = (entry.widthVw / 100) * window.innerWidth * scale;
|
||||
const heightPx = widthPx * (img.naturalHeight / img.naturalWidth);
|
||||
const bRect = entry.block.getBoundingClientRect();
|
||||
const blockTopAbs = bRect.top + window.scrollY;
|
||||
const blockLeftAbs = bRect.left + window.scrollX;
|
||||
const blockHeightPx = bRect.height;
|
||||
img.style.width = widthPx + 'px';
|
||||
if (entry.anchor === 'block-left-top') {
|
||||
// bird-2 remontée de 0.6× sa hauteur au-dessus du bloc.
|
||||
const top = blockTopAbs - heightPx * 0.6;
|
||||
if (window.innerWidth < 1080) {
|
||||
img.style.left = '-3vw';
|
||||
} else {
|
||||
img.style.left = (blockLeftAbs - widthPx * 0.5 - lcLeftAbs) + 'px';
|
||||
}
|
||||
img.style.top = (top - lcTopAbs) + 'px';
|
||||
img.style.right = '';
|
||||
} else if (entry.anchor === 'viewport-right') {
|
||||
// flower-3 : tronquée par le bord droit du viewport.
|
||||
const top = blockTopAbs + blockHeightPx / 3 - heightPx / 2;
|
||||
img.style.right = '-20px';
|
||||
img.style.left = '';
|
||||
img.style.top = (top - lcTopAbs) + 'px';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Décors latéraux parallax v2.
|
||||
// Cloner le .decor-tile autant de fois que nécessaire pour couvrir
|
||||
@@ -618,10 +920,14 @@
|
||||
window.addEventListener('load', () => {
|
||||
setupBackgroundTiles();
|
||||
setupColDecor();
|
||||
setupHomeDiapoDecor();
|
||||
setupHomeAboveDecor();
|
||||
setupHomeProjetDecor();
|
||||
setupPageDecor();
|
||||
initRellax();
|
||||
});
|
||||
|
||||
// Resize debouncé : recalcule le nombre de tiles + col-decor,
|
||||
// Resize debouncé : recalcule le nombre de tiles + tous les décors,
|
||||
// repart Rellax propre.
|
||||
let bgResizeTimer = null;
|
||||
window.addEventListener('resize', () => {
|
||||
@@ -629,6 +935,10 @@
|
||||
bgResizeTimer = setTimeout(() => {
|
||||
setupBackgroundTiles();
|
||||
setupColDecor();
|
||||
setupHomeDiapoDecor();
|
||||
setupHomeAboveDecor();
|
||||
setupHomeProjetDecor();
|
||||
setupPageDecor();
|
||||
initRellax();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
@@ -168,6 +168,9 @@ main:has(#block-erabletheme-views-block-projets-block-1) {
|
||||
background-color: white;
|
||||
border-bottom: 7px solid $fluo_green;
|
||||
position: relative;
|
||||
@media (min-width: $breakpoint_desktop) {
|
||||
margin-bottom: 4vh;
|
||||
}
|
||||
h2 {
|
||||
margin-top: 5vh !important;
|
||||
margin-bottom: 7vh;
|
||||
@@ -201,7 +204,12 @@ main:has(#block-erabletheme-views-block-projets-block-1) {
|
||||
@include main_text_content();
|
||||
}
|
||||
|
||||
p:first-of-type {
|
||||
// Margin-top sur le tout premier élément textuel du contenu,
|
||||
// peu importe son type (p / h1-h6) ET peu importe la profondeur
|
||||
// de wrapping (Drupal wrappe souvent les champs dans une ou deux
|
||||
// <div>). :first-child garantit qu'on ne matche que le tout
|
||||
// premier (vs :first-of-type qui matche le 1er de chaque type).
|
||||
:is(p, h1, h2, h3, h4, h5, h6):first-child {
|
||||
margin-top: 7vh;
|
||||
}
|
||||
|
||||
|
||||
@@ -146,9 +146,10 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 5vh;
|
||||
@media (min-width: $breakpoint_desktop) {
|
||||
margin-top: 10vh;
|
||||
margin-bottom: 10vh;
|
||||
margin-bottom: 4vh;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,9 @@ body {
|
||||
}
|
||||
}
|
||||
footer {
|
||||
z-index: 0;
|
||||
// Au-dessus de main pour que les ornements posés sur le footer
|
||||
// (cf. .footer-decor) passent par-dessus le bas de la colonne.
|
||||
z-index: 3;
|
||||
}
|
||||
#background {
|
||||
z-index: -1;
|
||||
|
||||
@@ -19,12 +19,38 @@
|
||||
margin-top: 50px;
|
||||
padding: 1rem $x_margin;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
@media (min-width: $breakpoint_desktop) {
|
||||
width: 50vw;
|
||||
margin-left: 25vw;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
// Ornements décoratifs (oiseau / fleur) "posés" sur le bord haut du
|
||||
// footer : bord bas légèrement entré dans le footer, le reste dépasse
|
||||
// au-dessus. En desktop le footer fait 50vw centré ; les ornements
|
||||
// sont à cheval sur les bords du footer (moitié dedans, moitié hors).
|
||||
// Le footer est passé au-dessus de main (cf. _global.scss) pour que
|
||||
// les ornements recouvrent le bas de la colonne.
|
||||
// pointer-events:none pour ne pas bloquer les liens du footer.
|
||||
> .footer-decor {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
width: 18vw;
|
||||
height: auto;
|
||||
transform: translateY(15%);
|
||||
pointer-events: none;
|
||||
@media (min-width: $breakpoint_desktop) {
|
||||
width: 8vw;
|
||||
}
|
||||
}
|
||||
> .footer-decor-bird { left: 0; }
|
||||
> .footer-decor-flower { right: 0; }
|
||||
@media (min-width: $breakpoint_desktop) {
|
||||
> .footer-decor-bird { left: -4vw; }
|
||||
> .footer-decor-flower { right: -4vw; }
|
||||
}
|
||||
|
||||
#footer_middle {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@@ -86,6 +86,10 @@
|
||||
</main>
|
||||
|
||||
<footer role="contentinfo">
|
||||
{# Ornements décoratifs au-dessus du footer (à cheval entre fullpage
|
||||
et footer). Purement visuels. #}
|
||||
<img class="footer-decor footer-decor-bird" src="/themes/erabletheme/assets/drawings/bird-3.png" aria-hidden="true" alt="">
|
||||
<img class="footer-decor footer-decor-flower" src="/themes/erabletheme/assets/drawings/flower-2.png" aria-hidden="true" alt="">
|
||||
<section id="footer_top">{{ page.footer_top }}</section>
|
||||
<section id="footer_middle">
|
||||
<section id="footer_left">{{ page.footer_left }}</section>
|
||||
|
||||