Curseurs✨ Premium

Cursor Ripple

Un verrou de 100 ms sur le mousemove cadence la naissance des ondulations : à chaque impulsion autorisée, un élément naît aux coordonnées du pointeur, s'anime via une classe CSS, et se détruit automatiquement 600 ms plus tard — jusqu'à six anneaux coexistent dans le DOM à vitesse soutenue.

JSRipple

Vous aurez accès à la démo interactive avec un compte gratuit.

Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Curseurs comprend 26 effets, dont 1 gratuits. Découvrir la catégorie →

3 exemples d'usage

Zone d'annotation sur un tableau de travail collaboratif — Cursor Ripple exemple 1

① Zone d'annotation sur un tableau de travail collaboratif

QuandUne surface partagée — carte, schéma, canvas — où plusieurs utilisateurs naviguent et où afficher les curseurs tiers surchargerait l'interface.
PourquoiLes ondulations matérialisent l'activité locale sans encombrer la vue : chaque bague naît, grandit et disparaît en 600 ms. Le rythme des 100 ms cale les anneaux sur la vitesse de lecture réelle, pas sur un minuteur indépendant.
RéglagesDurée de vie à 400 ms pour des anneaux plus vifs ; throttle à 150 ms pour limiter l'accumulation en session longue ; éléments insérés dans le conteneur scrollable en coordonnées relatives plutôt qu'au body.
Hero plein écran d'un portfolio créatif — Cursor Ripple exemple 2

② Hero plein écran d'un portfolio créatif

QuandUn hero sans CTA explicite — fond texturé, typographie seule — conçu pour être exploré à la souris avant de défiler. L'effet invite à la curiosité plutôt qu'à l'action.
PourquoiLes anneaux restent fixés à leur point de naissance pendant toute leur animation : une traversée rapide laisse un sillage décalé de rondelles qui s'estompent en cascade — un comportement qu'aucune traîne de curseur ne peut reproduire.
RéglagesDiamètre agrandi à 60–80 px dans la classe CSS, décalage ajusté en conséquence ; throttle à 70 ms pour densifier la constellation ; couleur en semi-transparent pour ne pas concurrencer la typographie.
Dropzone active en attente de dépôt de fichier — Cursor Ripple exemple 3

③ Dropzone active en attente de dépôt de fichier

QuandUne zone de glisser-déposer en état actif — l'utilisateur promène la souris pour confirmer que la surface est prête à recevoir son fichier.
PourquoiLes ondulations confirment la réceptivité de la zone sans texte ni animation de fond en boucle. Les anneaux suivent le rythme naturel du mouvement — plus honnête qu'une animation CSS figée qui tourne de la même façon quelle que soit l'interaction.
RéglagesThrottle à 180 ms pour un rythme posé ; durée de vie à 350 ms pour des bagues vives ; couleur synchronisée avec l'état de la dropzone (vert accepté, rouge refusé).

Comment ça marche

Avant toute création, le code passe par un filtre temporel. À chaque événement mousemove, il compare Date.now() à la variable lastRippleTime : si l'écart est inférieur à 100 ms (rippleThrottle), le gestionnaire retourne immédiatement. Ce délai règle le rythme de l'effet — il cadence les anneaux bien plus que la vitesse du curseur. À mouvement lent, les rondelles naissent espacées et s'épanouissent individuellement. À mouvement rapide, le filtre autorise dix impulsions par seconde au maximum. Quand le curseur s'immobilise, aucun mousemove ne circule : l'effet s'éteint de lui-même, sans nettoyage à prévoir.

À chaque impulsion passée le verrou, un <div> reçoit deux classes : custom-cursor cursor-ripple. La première fixe les propriétés de base des curseurs, notamment position: fixed ; la seconde porte les @keyframes — agrandissement de l'anneau et effacement de son opacité. L'élément est inséré dans document.body, pas dans la zone d'écoute. Ce choix est cohérent avec les coordonnées utilisées : e.clientX et e.clientY sont dans le viewport, et position: fixed les interprète dans ce même repère. Un décalage de −10 px sur chaque axe compense la moitié du diamètre implicite de l'anneau pour centrer son origine sur le hotspot du curseur.

Une fois inséré, l'élément est autonome. Un setTimeout de 600 ms programme son retrait via ripple.remove() — aucun registre des éléments en vol, aucune interruption si le curseur sort de la zone. Durée de vie (600 ms) divisée par throttle (100 ms) : au maximum, six anneaux coexistent simultanément dans le document. À vitesse soutenue, ils se chevauchent en traîne d'ondes ; à vitesse faible, chaque anneau achève seul sa course avant le suivant. Contrairement à une traîne de curseur, les bagues restent fixées à leur point de naissance — elles se propagent depuis l'endroit où le curseur était, pas depuis où il est.

Accessibilité

  • prefers-reduced-motion : absent du code. La classe cursor-ripple ne vérifie aucune préférence système. Conditionne la création côté JavaScript avec window.matchMedia('(prefers-reduced-motion: reduce)').matches avant l'insertion, ou enveloppe les @keyframes dans @media (prefers-reduced-motion: no-preference) côté CSS.
  • Sur écran tactile, mousemove ne se déclenche pas : aucune bague ne naît, aucun nettoyage n'est nécessaire. Pour des ondulations au tap, un écouteur touchstart distinct convient — mais chaque doigt générant son propre événement, un geste multi-touch crée plusieurs ondulations simultanées.
  • Les éléments atterrissent dans <body> sans aria-hidden — ces nœuds dynamiques peuvent être annoncés par le lecteur d'écran. Ajoute ripple.setAttribute('aria-hidden', 'true') après le createElement pour les exclure de l'arbre d'accessibilité.
  • Avec position: fixed, le scroll ne neutralise pas les ondulations existantes : elles restent ancrées au viewport jusqu'à l'expiration du timer. Réduis leur durée de vie ou confine-les dans la zone parente (voir Personnalisation) si la page scrolle.

Compatibilité navigateur

Repose sur les fonctions fléchées et const/let (ES2015), Date.now() (ES5), classList et setTimeout. Le plancher est dicté par les fonctions fléchées : IE 11 échoue avant le premier mousemove. Côté CSS, position: fixed et @keyframes sont acquis depuis 2012.

Chrome 45+✓ Complet
Firefox 22+✓ Complet
Safari 10+✓ Complet
Edge 15+✓ Complet
Mobile iOS✓ Inerte (pas de curseur)
Android Chrome✓ Inerte (pas de curseur)

Sans JavaScript, aucun élément n'est créé et la zone reste inerte — le repli est intégré à la conception. Sur IE 11, l'erreur de syntaxe sur les fonctions fléchées bloque le script ; si IE 11 est une cible, transpile-le : la logique n'a aucune autre dépendance.

Le code

Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :

index.html — structure
🔒 Débloquer le code complet — à partir de 2,99 € le 1er mois

HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.

Personnaliser

Options passées à l'API ou attributs data-* :

Option / propriétéDéfautEffet
Délai de cadence (rippleThrottle = 100) 100 ms Comparé à l'écart entre deux appels successifs à Date.now(). À 50 ms, le nombre maximal d'anneaux simultanés double (douze pour une durée de vie de 600 ms). À 200 ms, il descend à trois — chaque bague a la place de s'épanouir seule.
Durée de vie de l'anneau (setTimeout 600) 600 ms La durée du setTimeout et celle de l'animation @keyframes doivent rester alignées. Si l'animation dure 400 ms mais le timer 600 ms, l'élément reste 200 ms invisible dans le DOM. Descends sous 300 ms pour des éclats vifs ; monte à 900 ms pour des anneaux qui se fondent lentement.
Décalage de centrage (−10 px sur left et top) −10 px Compense la moitié du diamètre visuel de l'anneau. Si tu changes la taille de .cursor-ripple — par exemple width: 40px — le décalage doit passer à −20. Un mauvais décalage déplace le centre de la bague par rapport au hotspot du curseur.
Classe CSS de l'anneau (cursor-ripple) cursor-ripple Toute la partie visuelle — couleur, taille initiale, keyframes d'agrandissement et de fondu — vit dans cette classe. Crée des variantes (cursor-ripple--click, cursor-ripple--large) et alterne la classe selon le contexte sans modifier le JavaScript.
Cible d'insertion (document.body) document.body Insérer dans body avec position: fixed ancre les anneaux au viewport. Pour les confiner dans un conteneur scrollable, insère-les dans la zone parente (position: relative) et calcule des coordonnées relatives : e.clientX - rect.left et e.clientY - rect.top. Remplace aussi fixed par absolute dans la classe CSS.
Événement déclencheur (mousemove) mousemove Remplace par click pour des ondulations uniquement au clic — le throttle devient inutile, chaque clic produit exactement un anneau. Combine les deux en attachant deux écouteurs distincts avec des classes différentes : mouvement pour des petits anneaux fins, clic pour un grand anneau d'impact.

FAQ

La réponse est arithmétique : durée de vie (600 ms) divisée par throttle (100 ms) = 6 au maximum. Sur du contenu statique, six <div> animés par CSS sont négligeables. Sur une page lourde en transforms, monte le throttle à 150 ms (quatre éléments simultanés au lieu de six).
Parce que e.clientX et e.clientY sont des coordonnées viewport que position: fixed interprète dans ce même repère. Changer la cible sans adapter les coordonnées décale les anneaux par rapport au curseur. Pour les confiner dans la zone, calcule des coordonnées relatives et remplace fixed par absolute dans la classe CSS.
Non. Avec position: fixed, le scroll ne les déplace pas — elles restent ancrées au viewport jusqu'à expiration du setTimeout. Et le code ne pose aucun mouseleave : une bague créée en bord de zone finit ses 600 ms même si le curseur est sorti. Pour interrompre les animations en cours à la sortie de zone, collecte les id retournés par setTimeout et appelle clearTimeout dans un écouteur mouseleave.