Module « secours routier » validé
35 h de formation · évaluation finale 17/20
Douze carrés qui entrent l'un après l'autre, 0,1 s d'écart, en montant de 20 px et en grandissant de 0,8 à 1 : la cascade part quand le bloc arrive à l'écran, puis la grille reste en place — 1,3 Ko de JavaScript, vos propres éléments acceptés, posés d'emblée si l'utilisateur réduit les animations.
Mis à jour le
Cet effet est gratuit — la catégorie Transitions en contient 52 au total, dont 9 gratuits. Effect.Labs regroupe 840 effets vanilla. Découvrir la catégorie →
35 h de formation · évaluation finale 17/20
Quel jour vous arrange ?
Semaine du 3 novembre · centre de Lamballe
12 créneaux libres, du plus tôt au plus tard
Le jury a signé les dernières feuilles dimanche à 18 h.
Le cadre .md-stagger-stage mesure 250 px de haut, fond #1a1a24. Dedans, .md-stagger est une rangée flexible qui passe à la ligne (flex-wrap: wrap, espacement de 8 px, marge intérieure de 20 px). Chaque élément reçoit la classe .md-stagger-item : 40 × 40 px, coins de 8 px, dégradé indigo vers violet (#6366f1 → #a855f7). Il part invisible, 20 px plus bas et réduit à 0,8 (opacity: 0; transform: translateY(20px) scale(0.8)), et l'animation mdStaggerIn l'amène en 0,6 s à sa place, pleine taille ; forwards l'y laisse. Tout le décalage tient dans une ligne du script : animation-delay vaut i × 0,1 s. Le douzième carré démarre à 1,1 s et s'arrête à 1,7 s. Mesuré dans Chromium 300 ms après le départ : opacités 0,77 / 0,52 / 0,16 pour les trois premiers, 0 pour les neuf autres.
Le script parcourt chaque .md-stagger de la page. Si le conteneur a déjà des enfants, ce sont eux qui entrent en cascade ; s'il est vide, il crée 12 carrés de démonstration. La fonction play() retire puis remet la classe de chaque élément en forçant un recalcul entre les deux (void item.offsetWidth) : c'est ce qui relance une animation CSS déjà jouée. Un IntersectionObserver au seuil de 0,3 appelle play() quand 30 % du conteneur devient visible. Sortir de l'écran ne fait rien : la grille reste affichée. Y revenir rejoue la cascade. Mesuré dans Chromium, Firefox et WebKit : à 3,4 s puis à 6,4 s, les douze carrés sont toujours à opacité 1 ; après un aller-retour de défilement, 150 ms après le retour, opacités 0,35 / 0,04 / 0 — la cascade est repartie du début. Avec var once = true, l'observateur se déconnecte après la première entrée.
Pourquoi attendre l'écran plutôt que jouer au chargement ? Une cascade sert à faire lire une grille dans l'ordre : jouée hors de la vue, elle est finie avant que quelqu'un la regarde. Le conteneur reste vide tant qu'il n'est pas atteint, puis l'état final tient — un contenu qui disparaîtrait pour réapparaître toutes les trois secondes empêcherait de le lire. Le cadre a sa propre classe, préfixée comme le reste de l'effet : deux effets de la bibliothèque collés sur la même page ne se réécrivent pas. Une variante de la catégorie applique le même décalage, au clic, à la sortie et au retour d'une liste.
prefers-reduced-motion: reduce, le CSS retire l'animation et pose chaque élément à sa place (animation: none; opacity: 1; transform: none). Mesuré dans Chromium 151, Firefox 153 et WebKit 26 : les douze éléments sont à opacité 1 dès la première mesure, sans montée ni agrandissement.div vides : ils ne disent rien. Si la grille porte une information (des badges, des créneaux), écrivez-la dans les éléments eux-mêmes, pas dans un décor.CSS (@keyframes, animation-fill-mode: forwards, transform) + 1,3 Ko de JavaScript (IntersectionObserver, NodeList.forEach), aucune dépendance. Mesuré dans Chromium 151, Firefox 153 et WebKit 26 : mêmes délais, même grille tenue après la cascade, même relance au retour dans l'écran, même pose directe sous mouvement réduit. IntersectionObserver existe depuis Chrome 51, Firefox 55 et Safari 12.1.
Sans IntersectionObserver, le script joue la cascade une fois, au chargement. Sans JavaScript, le conteneur de démonstration reste vide ; vos propres éléments, s'ils portent déjà la classe md-stagger-item dans le HTML, apparaissent tous ensemble au chargement (même montée, sans décalage). Sans animations CSS, ils restent à opacité 0 : dans ce cas, n'ajoutez pas la classe dans le HTML et laissez le script la poser.
Copiez les trois blocs dans votre page. Aucune dépendance.
<div class="md-stagger-stage">
<div class="md-stagger"></div>
</div>
.md-stagger-stage,
.md-stagger-stage *,
.md-stagger-stage *::before,
.md-stagger-stage *::after {
box-sizing: border-box
}
.md-stagger-stage {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 250px;
overflow: hidden;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.md-stagger {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 20px;
flex-wrap: wrap;
}
.md-stagger-item {
width: 40px;
height: 40px;
border-radius: 8px;
background: linear-gradient(135deg, #6366f1, #a855f7);
opacity: 0;
transform: translateY(20px) scale(0.8);
animation: mdStaggerIn 0.6s forwards;
}
@keyframes mdStaggerIn {
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
/* Mouvement réduit : les éléments sont posés d'emblée, sans montée ni agrandissement */
@media (prefers-reduced-motion: reduce) {
.md-stagger-item {
animation: none;
opacity: 1;
transform: none;
}
}
(function() {
// Staggered Reveal : chaque .md-stagger de la page entre en cascade quand il
// arrive à l'écran, puis reste affiché. Il se rejoue s'il sort de l'écran et y revient.
var once = false; // true : la cascade ne se joue qu'à la première entrée
document.querySelectorAll('.md-stagger').forEach(function(container) {
// Vos éléments s'il y en a dans le conteneur, sinon 12 carrés de démonstration
var own = container.children.length > 0;
function play() {
if (!own) {
container.innerHTML = '';
for (var i = 0; i < 12; i++) container.appendChild(document.createElement('div'));
}
Array.prototype.forEach.call(container.children, function(item, i) {
item.classList.remove('md-stagger-item');
void item.offsetWidth; // relance l'animation
item.classList.add('md-stagger-item');
item.style.animationDelay = (i * 0.1) + 's';
});
}
if (!('IntersectionObserver' in window)) {
play();
return;
}
var obs = new IntersectionObserver(function(entries) {
if (!entries[0].isIntersecting) return;
play();
if (once) obs.disconnect();
}, {
threshold: 0.3
});
obs.observe(container);
});
})();
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Écart entre deux éléments (JS — i * 0.1) |
0,1 s | La durée totale vaut (nombre d'éléments − 1) × écart + 0,6 s. 0,05 s pour une grille de 20 éléments (1,55 s) ; 0,15 s pour quatre grandes cartes. |
Durée et courbe (CSS — animation: mdStaggerIn 0.6s forwards) |
0,6 s, courbe par défaut (ease) | 0,4 s rend la cascade plus sèche. Une courbe cubic-bezier(.2, .8, .2, 1) fait arriver chaque élément vite et se poser en douceur. |
Point de départ (CSS — transform: translateY(20px) scale(0.8)) |
20 px plus bas, 80 % de la taille | translateX(-16px) fait entrer depuis la gauche, utile pour une liste ; scale(0.5) seul donne un effet d'éclosion. Gardez l'état d'arrivée du @keyframes à zéro. |
Seuil d'entrée (JS — threshold: 0.3) |
30 % du conteneur visible | 0 lance dès le premier pixel visible, quitte à jouer la cascade sous le bord de l'écran ; 0,6 attend que la grille soit presque entière. |
Une seule fois (JS — var once = false) |
rejoue à chaque retour dans l'écran | true déconnecte l'observateur après la première cascade : la grille reste ensuite immobile, même après un aller-retour de défilement. |
Vos éléments (HTML — enfants de .md-stagger) |
aucun : 12 carrés de démonstration | Dès que le conteneur a des enfants, ce sont eux qui entrent en cascade, dans l'ordre du code. Donnez-leur width: auto; height: auto et leur propre fond pour remplacer le carré de 40 px. |
Parce qu'une cascade accompagne une arrivée : une fois la grille entrée, elle doit rester lisible. Le script la rejoue seulement quand le conteneur revient à l'écran (après un défilement, ou après avoir été masqué). Pour la relancer à la demande, après un changement de filtre par exemple, masquez un parent du conteneur (attribut hidden) puis réaffichez-le un peu plus tard : l'observateur voit la grille revenir et rejoue la cascade. C'est ce que font les trois exemples de cette page. L'attribut hidden posé sur .md-stagger lui-même ne suffit pas : son display: flex l'emporte ; ajoutez alors .md-stagger[hidden] { display: none }.
Placez-les dans .md-stagger : le script les trouve et leur applique la classe et le délai, dans l'ordre du HTML. Écrivez la classe md-stagger-item directement sur chaque carte : elles sont alors cachées dès le chargement, sans apparition furtive avant la cascade, et s'affichent même si le script ne se charge pas. Ajoutez width: auto; height: auto et votre fond pour remplacer le carré dégradé de 40 px ; la mise en page (grille, liste) est libre.
À 0,1 s d'écart, le 40e arriverait après 4,5 s : trop long. Plafonnez le délai — Math.min(i, 12) * 0.1 fait partir tous les éléments au-delà du 12e en même temps, à 1,2 s, pour être en place à 1,8 s — ou réduisez l'écart à 0,03 s. Pour une longue page, observez plutôt chaque bloc séparément : un .md-stagger par section, chacun joue sa cascade quand il arrive à l'écran.