Animation de défilement style Apple
Les effets sont contrôlés par la position de défilement, pas par un minuteur. Le défilement vers le haut les lit en sens inverse. Aucune bibliothèque nécessaire.
Les trois idées
- Épingler. Un conteneur haut (par ex.
height: 300vh) contient un enfantposition: sticky; top: 0; height: 100vh; overflow: hidden. L'enfant reste à l'écran pendant que le conteneur défile. La hauteur supplémentaire du conteneur constitue votre budget d'animation. - Progression. Une fonction transforme la position de défilement en un nombre 0..1 :
- section épinglée :
clamp(-rect.top / (rect.height - innerHeight)) - élément entrant dans la fenêtre :
clamp((innerHeight*a - rect.top) / (innerHeight*(a-b)))
- section épinglée :
- Pilotez les visuels à partir du nombre. Définissez
translate,rotate,scale,opacity(etborderRadius) à chaque image. Un seul écouteurscrollrégulé avecrequestAnimationFramerecalcule tout.
Moteur prêt à coller : references/scroll-engine.js.
Effets qui fonctionnent bien (choisissez-en 3 à 5, pas tous)
| Effet | Comment faire |
|---|---|
| Assemblage | Les éléments commencent dispersés autour des bords de la fenêtre (angle aléatoire, rotation, échelle .35, opacité .2) et rejoignent leur position finale. Calculez le décalage de chaque élément par rapport à sa place naturelle une fois (au chargement/redimensionnement), puis multipliez par (1 - ease(p)). Décalez par index. |
| Illumination de mots | Enveloppez chaque mot dans une balise span. Opacité .18 → 1 avec clamp(p * 1.25 * N - k). |
| Stepper épinglé | step = floor(p * N) ; la fraction p*N - step remplit une barre de progression à l'intérieur de l'étape active. Cliquer sur une étape doit faire scrollTo vers sa position. |
| Inclinaison | perspective(1400px) rotateX((1-e)*22deg) translateY((1-e)*60px) scale(.84 + .16*e) pour une maquette de téléphone/appareil. |
| Cartes ascendantes | translate: 0 (1-e)*80px; scale: .9+.1e; opacity: e, retard par colonne (i % cols). |
| Expansion | Vidéo/image scale .78 → 1 et borderRadius 44px → 28px. |
Utilisez ease = t => 1 - (1-t)**3 sur la progression avant de l'appliquer.
Règles (apprises à la dure)
- Utilisez les propriétés CSS individuelles
translate,rotate,scalepour le scrubbing, et gardeztransformpour le survol. Sinon, le survol et le défilement se disputent la même propriété. - Ne combinez pas une classe
.revealde fondu avec une transformation scrubbée sur le même élément. Choisissez-en une seule. - Placez le titre de la section à l'intérieur du panneau épinglé avec le contenu. Si le panneau ne contient que le contenu centré, les écrans hauts affichent une grande zone vide.
- Gardez la distance de défilement modeste. Environ
N * 55vh + 100vhpour un stepper à N étapes,240-300vhpour un effet unique. Les utilisateurs trouvent 700vh+ fatigant. - Limitez ce qui est épinglé à ce qui tient dans 100vh. Sur les écrans courts (
max-height: 820px) et les téléphones, masquez les sous-titres et les extras, et n'affichez que l'étape active dans un stepper. - Mouvement réduit : si
prefers-reduced-motion: reduce, renvoyez une progression1partout (état final, pas de mouvement). Ne laissez pas les éléments cachés. - Sticky ne nécessite pas
overflowsur les ancêtres saufoverflow-x: hiddensurbody, ce qui est acceptable. Évitezoverflow: hiddensur tout conteneur entre l'élément sticky et la page. - Recalculez lors du redimensionnement (les décalages d'assemblage dépendent de la mise en page).
- Animez uniquement
translate/rotate/scale/opacity/border-radius; évitez les propriétés de mise en page. - Évitez un premier/dernier segment de vidéo minuscule : si vous scrubbiez la lecture
<video>, les courts extraits peuvent s'afficher vides.
Ordre typique de la page
Hero (épinglé, assemblage) → une déclaration simple (sans effet) → preuve/film (expansion) → stepper épinglé → maquette d'appareil (inclinaison) → grille de cartes (ascension) → CTA. Alternez les sections noires et gris clair pour l'ambiance Apple ; une couleur d'accent.
Test
Le --screenshot de Chrome sans tête ne peut pas défiler. Pilotez un vrai Chrome via le protocole DevTools à la place :
chrome --headless=new --remote-debugging-port=9333 --user-data-dir=/tmp/prof about:blank &- Depuis Node (v22+ a
WebSocketglobal) : fetchhttp://localhost:9333/json, ouvrez lewebSocketDebuggerUrlde la page, envoyezEmulation.setDeviceMetricsOverride,Page.navigate, puis pour chaque positionRuntime.evaluate("scrollTo(0,Y)"), attendez ~700 ms,Page.captureScreenshot. - Choisissez Y à partir des
offsetTopet hauteurs des sections (par ex. 0%, 60%, 100% de chaque section épinglée). - Vérifiez sur desktop (1440x900), un grand écran (2000x1300) et un téléphone (390x800), et confirmez
document.documentElement.scrollWidth === innerWidth(pas de débordement horizontal).
Liste de contrôle de livraison
- Chaque effet est piloté par la progression du défilement et réversible
- Fallback pour mouvement réduit
- Mise en page téléphone vérifiée, pas de défilement horizontal
- Longueur de défilement épinglée modeste ; pas d'espace vide sur les écrans hauts
- Captures d'écran prises au début / milieu / fin de chaque section épinglée