Restructure the page so the first 4 viewports of scroll drive a Three.js scene composited on top of the Antananarivo parachute video. What's there: - Three.js (ESM, r158 via importmap) renders a low-poly cargo airliner built from primitives: cylinder fuselage, cone nose, sphere cockpit (dark glass + emissive), box wings/tail/fin, cylinder engines with torus intakes, gold trim band, navy fin with gold logo box. No external model file. - Hemisphere + directional + ambient lights tuned for golden-hour fill. - 14 cloud spheres scattered around the plane, slowly rotating. - GSAP + ScrollTrigger drive a single progress value scrubbed against scroll position. Inside the rAF loop, the camera arcs from rear-left (-0.6 rad) to front-right (+1.1 rad), radius dipping mid-flight, and the plane rolls slightly with scroll. - Three act labels (Paris CDG / Vol cargo / Antananarivo) cross-fade at 20%/40%-60%/72% scroll positions via a chained gsap timeline. - Gold CTA button stays opacity:0 + pointer-events:none until the last ~10% of scroll, then fades and scales in. Hover transform rebuilt without the old mouse-parallax tilt (fights the scroll animation). - Scroll hint pill (chevron + "Faites défiler") at the bottom of the first viewport, fades out on first scroll event. - prefers-reduced-motion shortcut: scroll stage hidden, CTA visible, no animation. Page reverts to a static screen with the video bg. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
115 lines
4.5 KiB
HTML
115 lines
4.5 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>MVA Global Fret — Bienvenue</title>
|
|
<meta name="description" content="MVA Global Fret — Le pont aérien entre Paris et Antananarivo.">
|
|
<link rel="icon" type="image/png" href="PNG MVA GLOBAL FRET.png">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Poppins:wght@600;700;800&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
|
<link rel="stylesheet" href="css/parallax.css">
|
|
|
|
<!-- Three.js (ESM via importmap) -->
|
|
<script type="importmap">
|
|
{
|
|
"imports": {
|
|
"three": "https://cdn.jsdelivr.net/npm/three@0.158.0/build/three.module.js"
|
|
}
|
|
}
|
|
</script>
|
|
<!-- GSAP + ScrollTrigger (UMD) -->
|
|
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
|
|
</head>
|
|
<body class="parallax-body">
|
|
|
|
<header class="parallax-header">
|
|
<div class="parallax-logo" aria-label="MVA Global Fret">
|
|
<img src="PNG MVA GLOBAL FRET.png" alt="MVA Global Fret">
|
|
<span>MVA Global Fret</span>
|
|
</div>
|
|
<div class="lang-switcher" role="group" aria-label="Choisir la langue">
|
|
<button data-lang="fr" class="active">FR</button>
|
|
<button data-lang="en">EN</button>
|
|
<button data-lang="mg">MG</button>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Couches fixes : vidéo + voile + canvas Three.js (le plan 3D vole dessus) -->
|
|
<div class="stage-fixed">
|
|
<video class="layer layer-video" id="introVideo"
|
|
autoplay loop muted playsinline preload="auto"
|
|
poster="videos/parachute-poster.jpg">
|
|
<source src="videos/parachute-drop.mp4" type="video/mp4">
|
|
</video>
|
|
|
|
<div class="layer layer-tint"></div>
|
|
|
|
<canvas id="three-canvas" class="layer layer-three"></canvas>
|
|
|
|
<!-- Étiquettes des actes — apparaissent / disparaissent selon le scroll -->
|
|
<div class="act-label" id="label-paris"><span>Paris · CDG</span></div>
|
|
<div class="act-label" id="label-cruise"><span>Vol cargo<br>10 000 km</span></div>
|
|
<div class="act-label" id="label-tana"><span>Antananarivo</span></div>
|
|
|
|
<!-- Indicateur scroll (visible jusqu'à ce qu'on scrolle) -->
|
|
<div class="scroll-hint" id="scrollHint">
|
|
<span data-i18n="intro.scrollHint">Faites défiler</span>
|
|
<i class="fa-solid fa-chevron-down"></i>
|
|
</div>
|
|
|
|
<!-- CTA — caché jusqu'à la fin du scroll -->
|
|
<a href="accueil.html" class="cta-btn" id="ctaBtn">
|
|
<span class="cta-btn-shine"></span>
|
|
<span data-i18n="intro.ctaBtn">Accéder au site</span>
|
|
<i class="fa-solid fa-arrow-right"></i>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Sentinelles invisibles : leur scroll-position pilote la timeline -->
|
|
<main class="scroll-stage" aria-hidden="true">
|
|
<section class="act"></section>
|
|
<section class="act"></section>
|
|
<section class="act"></section>
|
|
<section class="act"></section>
|
|
</main>
|
|
|
|
<script src="js/translations.js"></script>
|
|
<script>
|
|
/* i18n minimal ------------------------------------------------------- */
|
|
(function () {
|
|
const lang = localStorage.getItem('mva-lang') || 'fr';
|
|
applyLang(lang);
|
|
document.querySelectorAll('.lang-switcher button').forEach(btn => {
|
|
btn.addEventListener('click', () => applyLang(btn.dataset.lang));
|
|
});
|
|
function applyLang(l) {
|
|
document.documentElement.lang = l;
|
|
localStorage.setItem('mva-lang', l);
|
|
document.querySelectorAll('.lang-switcher button').forEach(b =>
|
|
b.classList.toggle('active', b.dataset.lang === l)
|
|
);
|
|
const t = window.translations?.[l];
|
|
if (!t) return;
|
|
document.querySelectorAll('[data-i18n]').forEach(el => {
|
|
const keys = el.dataset.i18n.split('.');
|
|
let v = t;
|
|
for (const k of keys) v = v?.[k];
|
|
if (v != null) el.textContent = v;
|
|
});
|
|
}
|
|
})();
|
|
|
|
/* Scroll-hint : disparaît dès qu'on commence à scroller */
|
|
window.addEventListener('scroll', () => {
|
|
document.getElementById('scrollHint')?.classList.add('hidden');
|
|
}, { once: true, passive: true });
|
|
</script>
|
|
|
|
<script type="module" src="js/intro-scene.js"></script>
|
|
</body>
|
|
</html>
|