Zoom & Blur
Trois images-clés symétriques sur animation-timeline: scroll() dans une boîte de 280 px : le cercle n'est net, entier et à ses 200 px qu'à mi-course (260 px sur 520) — ×0,5, flou 10 px, opacité .3 avant ; ×2, flou 10 px, .3 après. Un point de mise au point, pas un zoom. Zéro JavaScript.
Mis à jour le
Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Scroll comprend 70 effets, dont 12 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
Quatre éléments : le cadre .demo-preview (flex centré, min-height: 300px, position: relative, fond #0a0a0f, texte blanc, sans-serif), la boîte .sdc-scroll-container (100% × 280px, overflow-y: auto, scroll-behavior: smooth, barre de 4 px en ::-webkit-scrollbar), le plateau .sdc-zoom-content (800 px, flex centré) et le cercle .sdc-zoom-element : 200 × 200 px, border-radius: 50%, dégradé 135° #8b5cf6 → #ec4899, sans texte. Deux étiquettes en dur : .sdc-zoom-text « Zoom in → net → zoom out » (11,5 px, blanc à 30 %, à 40 px du bas du plateau) et .sdc-scroll-hint « Scrollez ↓ » (10,4 px, ancrée au cadre, pulsation .3 → .7 en 2 s). Course : 800 − 280 = 520 px. Champ JavaScript vide, aucun var(--…), aucun id ; mesuré dans une page vierge (fond blanc et sombre, 1 280 et 390 px) : aucune requête, aucune erreur console dans Chromium 151, WebKit 26.5 et Firefox 153.
Une règle fait tout : animation-name: sdcZoomBlur avec animation-timeline: scroll() — l'animation n'avance plus avec le temps mais avec la position du premier ancêtre défilant, la boîte ; animation-duration: auto et animation-range: normal l'étalent sur toute la course, linear relie les images en droite. Trois images-clés : 0% { transform: scale(.5); filter: blur(10px); opacity: .3 }, 50% { transform: scale(1); filter: blur(0); opacity: 1 }, 100% { transform: scale(2); filter: blur(10px); opacity: .3 }. Mesuré par crans de 100 px : ×0,69 / 6,2 px / .57, ×0,88 / 2,3 px / .84, ×1,15 / 1,5 px / .89, ×1,54 / 5,4 px / .62, ×1,92 / 9,2 px / .35, puis ×2 / 10 px / .3 ; à 260 px exactement, ×1 / 0 / 1. Flou et opacité varient au même rythme des deux côtés (3,8 px et .27 par 100 px), pas l'échelle : +0,19 par 100 px avant le point net (100 → 200 px), +0,38 après (200 → 400 px). Le net existe à une seule position, et c'est aussi l'état de base de l'élément : là où la timeline n'existe pas, c'est lui qui s'affiche.
Le cercle est centré à 400 px du haut d'un plateau dont la boîte montre 280 : au repos, réduit à 100 px (350–450 px), il est sous le pli — boîte sombre vide, plus l'indice. Mesuré : il entre par le bas à 59 px de défilement, est centré et net à 260 px avec 40 px de marge, grossit et sort par le haut — à 520 px, ses 400 px vont de −320 à 80 px, il reste une calotte floue de 80 px à 30 %, et l'étiquette n'apparaît que sur les 41 derniers pixels. Point net et centrage sont liés : l'image 50 % tombe à mi-course, le plateau met le cercle à mi-hauteur, donc au centre de la boîte à cet instant ; déplacer l'un sans l'autre donne un objet net hors cadre. En largeur, ×2 fait 400 px : sous 400 px de boîte, overflow-y: auto (qui implique overflow-x: auto) ouvre une piste horizontale — mesuré 13 px à 390 px de large. scroll-behavior: smooth ne lisse que les positions posées par script ou ancre. Sans id ni script, plusieurs copies par page pilotent chacune leur boîte.
Accessibilité
- prefers-reduced-motion absent du code : mesuré sous émulation, mêmes valeurs (×0,5 → ×2, flou 10 px), indice toujours pulsant. Rien n'est caché — cercle visible dès 59 px, net à 260 — et rien ne bouge sans le geste du lecteur (WCAG 2.3.3). Le point sensible est le flou : 10 px sur un contenu réel le rendent illisible sur deux tiers de la course. Ajoutez
@media (prefers-reduced-motion: reduce) { .sdc-zoom-element { animation: none } .sdc-scroll-hint { animation: none } }: le cercle reste alors à son état de base, 200 px, net, opaque — ce que voit Firefox. - Clavier : mesuré, Tab focalise la boîte sans
tabindexdans Chromium (anneauoutline: auto 1px) et Firefox 153 ; flèche bas = 40 px (51 dans Firefox), PageDown et Espace = 245 (252), Fin = 520, Début = 0. WebKit 26.5 ne la focalise pas sanstabindex="0"(focus surbody, flèches inertes ; 40 px par flèche avec l'attribut) : ajoutez-le, avec un:focus-visibleet unaria-label. Aucun cran ne s'arrête sur le point net (245 puis 490 px) : au clavier on ne voit jamais l'objet net — voir « Tenue du point net ». - Contraste : l'indice « Scrollez ↓ » (10,4 px, blanc à 30 % sur
#0a0a0f) vaut 2,6:1 au repos, de 1,2:1 à 1,8:1 sous la pulsation (l'opacité .3 → .7 se multiplie aux 30 % de la couleur) ; l'étiquette (11,5 px, 30 %) reste à 2,6:1. Décoratifs :aria-hidden="true", ou 70 % (9,7:1). Le cercle ne porte aucun texte ; un contenu à lire n'y est net qu'autour de 260 px, le flou dépasse 5 px avant 130 et après 390 — écrivez l'information aussi hors du cercle. - Lecteurs d'écran et repli : le cercle est un
divvide, rien à annoncer ; s'il devient une image porteuse de sens (lot, bague, lame), donnez-luirole="img"et unaria-label— un lecteur d'écran ne fait pas la mise au point. Les deux étiquettes sont lues telles quelles, en français et en franglais. Sansanimation-timeline(Firefox, Chrome avant 115), le cercle est à 200 px, net, opaque, et défile avec le plateau : tout reste visible et atteignable, sans effet.
Compatibilité navigateur
CSS seul : animation-timeline: scroll(), animation-duration: auto, animation-range (Scroll-driven Animations), filter: blur(), transform: scale(), opacity, overflow-y: auto, scroll-behavior, flexbox, @keyframes. Zéro JavaScript, zéro dépendance. Chrome et Edge 115+, Safari et iOS 26+ (mesuré dans WebKit 26.5 : mêmes valeurs que Chromium). Firefox : aucune version. Mesuré dans Firefox 153 : CSS.supports() répond faux pour animation-timeline: scroll(), animation-duration: auto et animation-range, les trois déclarations sont ignorées ; l'animation dure 0 s sans remplissage (animation-fill-mode: none) et l'élément retombe sur son état de base : transform: none, filter: none, opacity: 1. Le visiteur Firefox voit un cercle de 200 px, net et opaque, qui traverse la boîte comme un contenu ordinaire — entré à 20 px, centré à 260, sorti à 500 — puis l'étiquette. Rien de caché, rien de manquant : pas d'effet. Aucun @supports dans le code vendu ; @supports not (animation-timeline: scroll()) { .sdc-zoom-element { animation: none } } donnerait exactement le même rendu.
Sans animation-timeline (Firefox, Chrome et Edge avant 115, Safari avant 26) : cercle à 200 px, net, opaque, qui défile avec le plateau — tout visible, rien ne bouge. Sans filter, zoom et fondu subsistent. Là où overflow n'est pas honoré (clients mail), le plateau de 800 px s'affiche entier, cercle net au milieu.
Le code
Structure HTML à coller dans votre page (le CSS + JS sont inclus avec Premium) :
<div class="demo-preview">
<div class="sdc-scroll-container">
<div class="sdc-zoom-content">
<div class="sdc-zoom-element"></div>
<span class="sdc-zoom-text">…</span>
</div>
</div>
<span class="sdc-scroll-hint">…</span>
</div>
Le code complet HTML + CSS + JS de Zoom & Blur, prêt à coller — et les 811 effets. Ou 299 € une fois, accès à vie.
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Images-clés (CSS — @keyframes sdcZoomBlur) |
×0,5 / 10 px / .3 → ×1 / 0 / 1 → ×2 / 10 px / .3 | Trois valeurs indépendantes : scale(.85) → 1 → 1.25 pour une mise au point discrète, blur(4px) pour un flou plus photographique, opacity: .6 pour garder l'objet présent aux extrémités. Gardez l'image 50 % à ×1 / 0 / 1 : c'est l'état de base, celui de Firefox et de reduced-motion. |
Position du point net (CSS — l'image 50%, et le centrage du plateau) |
50 % = 260 px, cercle centré à 400 px du plateau | Mesuré avec l'image à 35% : net à 182 px, mais cercle à 118–318 px de la boîte, pas centré (centre à 218 au lieu de 140). Centre de l'objet = course × fraction + 140 px, soit 322 px pour 35 % — padding-top sur le plateau ou margin-top négatif sur le cercle. |
Tenue du point net (CSS — deux images-clés 40%, 60%) |
aucune : net sur une seule position | Mesuré avec 40%, 60% { transform: scale(1); filter: blur(0); opacity: 1 } : net de 208 à 312 px, 104 px de course au lieu d'une position — un cran de flèche (40 px) ne peut plus le manquer. Le flou reprend à 3,75 px 130 px de part et d'autre. |
Course et plage (CSS — .sdc-zoom-content { height: 800px }, .sdc-scroll-container { height: 280px }, animation-range) |
520 px, animation-range: normal |
Course = plateau − boîte ; le point net reste à mi-course. 1 200 px de plateau = 920 px de course, mouvement deux fois plus lent ; une boîte plus haute montre le cercle plus tôt (à 280 px, il n'entre qu'après 59 px). animation-range: 20% 80% concentre l'animation au milieu, mais hors plage l'élément retombe à l'état de base (mesuré : 200 px net à 65 px, saut à 100 px flou à 104) — ajoutez animation-fill-mode: both. |
Rythme (CSS — animation-timing-function: linear) |
linear | S'applique entre chaque paire d'images. Mesuré en ease-in-out : ×0,96 / 0,8 px / .94 à 208 px (contre ×0,9 / 2 px / .86 en linear), ×1,08 à 312 — le cercle s'attarde près du net et se précipite vers les extrémités. |
Taille, forme et contenu (CSS — .sdc-zoom-element { width/height: 200px; border-radius: 50% }) |
200 px, cercle, dégradé #8b5cf6 → #ec4899 | Tout le contenu (img, SVG, enfants) est zoomé et flouté avec la boîte : une image de 200 px est étirée à 400 px en bout — fournissez 400 px de source. Sous 400 px de boîte, débordement horizontal (mesuré 13 px à 390 px) : overflow-x: hidden. Sans id, plusieurs instances par page fonctionnent. |
Défileur et textes (CSS — scroll(), scroll-behavior: smooth, .sdc-scroll-hint, .sdc-zoom-text) |
défileur le plus proche, smooth, « Scrollez ↓ » et « Zoom in → net → zoom out » | scroll() prend le premier ancêtre défilant ; scroll(root) pilote par la page, view() par la traversée de la fenêtre (voir la FAQ). smooth ne lisse que les positions posées par script ou ancre. Les deux textes sont en dur (l'étiquette en franglais, visible sur les 41 derniers pixels) : traduisez-les ou supprimez les deux span. |
FAQ
animation-range: 20% 80% avec animation-fill-mode: both le tient à ×0,5 flou tant qu'il n'est pas dans la boîte. Le piège : remonter le cercle sans toucher aux images-clés — net à 260 px, mais 140 px trop haut, coupé par le bord.animation-timeline: scroll(), linear, animation-range: normal, pas de JavaScript, même sort sur Firefox. Mais fx-0590 n'a que deux images-clés, scale(1) → scale(2.2) et border-radius: 20px → 4px sur un carré de 120 px, sans filter ni opacity : mesuré ×1,6 à 260 px et ×2,2 à 520, où il ne reste que 12 px du carré dans la boîte — le plus grand est le dernier état, sans sticky malgré son nom. fx-0563 ajoute la troisième image, le flou et l'opacité : l'objet n'est lisible qu'à une position et brumeux des deux côtés — un point de repos pour l'œil, une mise au point, pas un zoom. fx-0590 pour faire grandir un objet jusqu'au bout (une révélation), fx-0563 pour le faire passer — apparaître, se lire, s'effacer — entre deux blocs de contenu.scroll(root) prend le défileur du document, mais la course devient la page entière et le point net tombe à 50 % de la page, rarement là où est l'objet. La bonne réponse est animation-timeline: view() : la timeline devient la traversée de la fenêtre par l'élément — 0 % quand il entre par le bas, 100 % sorti par le haut, 50 % au centre. Mesuré avec cette seule ligne changée, dans une page de 3 100 px : ×0,7 / 6 px / .58 à 200 px de son entrée, ×0,95 / 1 px / .93 à 50 px du centre, ×1,4 / 4 px / .72 en remontant, ×1,9 / 9 px / .37 avant de sortir. Plus de boîte ni de plateau : supprimez .sdc-scroll-container et .sdc-zoom-content, gardez overflow-x: hidden sur la page pour les 400 px de large en bout de course. Même prise en charge : Chrome 115, Safari 26, pas Firefox.