CTA emerges from the plane when it reaches center

Hide the gold pill button at page load (opacity 0, scale 0.05,
translated 27 vh up — roughly where the plane is when it crosses
the viewport center). When the plane's progress reaches 0.5, the
tick loop adds a `.revealed` class to .cta-btn; CSS variables flip
and a 1.2 s spring transition lands the button at viewport center
at full size.

Pulse halo (::after) is dormant until the .revealed class lands,
so it doesn't waste cycles on a hidden element. Hover scale (1.04)
re-introduced on `.cta-btn.revealed:hover` with the original 0.32 s
transition so it doesn't fight the slow emerge tween.

Plane and parcel logic untouched.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
MVA Global Fret 2026-05-05 14:07:48 +02:00
parent 4b622a7d85
commit 22c57e5b41
2 changed files with 48 additions and 7 deletions

View File

@ -117,12 +117,23 @@ html, body {
.layer-three { display: block; z-index: 2; }
/* ── BOUTON CTA centré ──────────────────────────────────────────────────── */
/* BOUTON CTA centré
État caché : positionné l'avion est au centre de sa course
(~27% au-dessus du centre du viewport), à scale 0. Quand le JS
ajoute la classe `.revealed` (au moment l'avion atteint p 0.5),
les variables CSS basculent et le bouton « sort » de l'avion en
grossissant jusqu'à sa taille finale au centre. */
.cta-btn {
--cta-y: -27vh; /* offset vertical : 0 = centré */
--cta-scale: 0.05;
--cta-opacity: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transform: translate(-50%, calc(-50% + var(--cta-y))) scale(var(--cta-scale));
opacity: var(--cta-opacity);
pointer-events: none;
z-index: 10;
display: inline-flex;
align-items: center;
@ -142,12 +153,25 @@ html, body {
0 20px 60px rgba(197, 165, 90, 0.55),
0 0 0 0 rgba(197, 165, 90, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.45);
transition: box-shadow 0.32s cubic-bezier(0.2, 0.8, 0.2, 1),
transform 0.32s cubic-bezier(0.2, 0.8, 0.2, 1);
transition:
transform 1.2s cubic-bezier(0.34, 1.5, 0.64, 1),
opacity 0.5s ease 0.05s,
box-shadow 0.32s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.cta-btn:hover {
transform: translate(-50%, -50%) scale(1.04);
.cta-btn.revealed {
--cta-y: 0vh;
--cta-scale: 1;
--cta-opacity: 1;
pointer-events: auto;
}
/* Hover seulement après la révélation, avec une transition rapide */
.cta-btn.revealed:hover {
--cta-scale: 1.04;
transition:
transform 0.32s cubic-bezier(0.2, 0.8, 0.2, 1),
box-shadow 0.32s cubic-bezier(0.2, 0.8, 0.2, 1);
box-shadow:
0 28px 75px rgba(197, 165, 90, 0.7),
0 0 0 12px rgba(197, 165, 90, 0.12),
@ -177,9 +201,12 @@ html, body {
inset: -3px;
border-radius: 50px;
border: 2px solid rgba(197, 165, 90, 0.55);
animation: ctaPulse 2.8s ease-out infinite;
animation: none; /* halo dormant tant que le bouton n'est pas révélé */
pointer-events: none;
}
.cta-btn.revealed::after {
animation: ctaPulse 2.8s ease-out 1s infinite; /* démarre après l'arrivée du bouton */
}
@keyframes ctaPulse {
0% { transform: scale(1); opacity: 0.7; }
100% { transform: scale(1.18); opacity: 0; }

View File

@ -239,6 +239,13 @@ window.addEventListener('deviceorientation', (e) => {
const root = document.documentElement;
/* Révélation du CTA
Quand l'avion arrive au centre de l'écran (p 0.5), on bascule la
classe `.revealed` sur le bouton. La CSS gère l'animation (translate
du haut vers le centre + scale 0.05 1, ~1.2 s). */
const ctaBtn = document.querySelector('.cta-btn');
let ctaRevealed = false;
/* ── Render loop ───────────────────────────────────────────────────────── */
const clock = new THREE.Clock();
@ -278,6 +285,13 @@ function tick() {
}
updateParcels(dt, t);
/* Au moment où l'avion atteint le centre, on déclenche la sortie du
CTA (l'animation CSS s'en occupe ensuite). One-shot. */
if (!ctaRevealed && p >= 0.5) {
ctaRevealed = true;
ctaBtn?.classList.add('revealed');
}
/* Avec nez à -X et up = +Y :
- rotation.z = PITCH (négatif = nez en l'air)
- rotation.x = ROLL