From a4f03a21aa0e5d41678946c623384281f1f7c76e Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Sat, 27 Jun 2026 10:36:51 +0200 Subject: [PATCH] Accueil: carrousel 3 cartes avec rotation en fondu toutes les 20s Remplace le defilement continu par 3 cartes fixes et propres. Toutes les 20 s, les cartes disparaissent une a une (fondu decale) puis les 3 suivantes apparaissent une a une. Rotation equitable sur tous les adherents actifs. Respecte prefers-reduced-motion (bascule sans fondu). Co-Authored-By: Claude Opus 4.8 --- src/app/globals.css | 34 ----------------- src/components/MembersCarousel.tsx | 60 +++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index 48917a8..f154bf3 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -352,37 +352,3 @@ button { margin-bottom: 6px; } -/* ---- Carrousel d'adhérents (défilement automatique) ---- */ -@keyframes pleinr-marquee { - from { - transform: translateX(0); - } - to { - transform: translateX(-50%); - } -} -.pleinr-marquee { - overflow: hidden; - position: relative; - -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent); - mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent); -} -.pleinr-marquee-track { - display: flex; - gap: 18px; - width: max-content; - animation-name: pleinr-marquee; - animation-timing-function: linear; - animation-iteration-count: infinite; -} -.pleinr-marquee:hover .pleinr-marquee-track { - animation-play-state: paused; -} -@media (prefers-reduced-motion: reduce) { - .pleinr-marquee-track { - animation: none; - } - .pleinr-marquee { - overflow-x: auto; - } -} diff --git a/src/components/MembersCarousel.tsx b/src/components/MembersCarousel.tsx index ba8032e..5afc23f 100644 --- a/src/components/MembersCarousel.tsx +++ b/src/components/MembersCarousel.tsx @@ -1,5 +1,6 @@ "use client"; +import { useEffect, useState } from "react"; import Link from "next/link"; export type CarouselMember = { @@ -17,15 +18,18 @@ export type CarouselMember = { const STRIPE_WARM = "repeating-linear-gradient(45deg,#efe9da,#efe9da 12px,#e6ddc9 12px,#e6ddc9 24px)"; -function Card({ m }: { m: CarouselMember }) { +const VISIBLE = 3; +const INTERVAL_MS = 20_000; +const STAGGER_MS = 220; // décalage de disparition/apparition entre cartes +const FADE_MS = 450; + +function Card({ m, visible, index }: { m: CarouselMember; visible: boolean; index: number }) { const image = m.coverUrl || m.logoUrl; return (
VISIBLE; + + useEffect(() => { + if (!canRotate) return; + // Respecte la préférence de réduction d'animation. + const reduce = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches; + + const tick = setInterval(() => { + if (reduce) { + setStart((s) => (s + VISIBLE) % total); + return; + } + // Disparition une à une, puis changement, puis réapparition une à une. + setVisible(false); + const outMs = FADE_MS + STAGGER_MS * (VISIBLE - 1); + setTimeout(() => { + setStart((s) => (s + VISIBLE) % total); + setVisible(true); + }, outMs); + }, INTERVAL_MS); + + return () => clearInterval(tick); + }, [canRotate, total]); + + if (total === 0) return null; + + // Fenêtre de 3 cartes, avec bouclage propre si on dépasse la fin. + const shown = Array.from({ length: Math.min(VISIBLE, total) }, (_, i) => members[(start + i) % total]); return ( -
-
- {loop.map((m, i) => ( - - ))} -
+
+ {shown.map((m, i) => ( + + ))}
); }