Emoji Cursor
Cinq émojis dans un tableau, un index qui avance à chaque clic : dès que la souris entre dans la zone, le curseur système s'efface et un caractère Unicode le remplace, repositionné à chaque mousemove sur les coordonnées exactes du pointeur.
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



Comment ça marche
L'effet mobilise deux acteurs distincts : la zone de détection (#zone-emoji, 300 px de haut) et un élément flottant (#cursorEmoji) positionné hors du flux du document. Dès que mouseenter se déclenche sur la zone, deux opérations ont lieu en même temps : la zone reçoit la classe cursor-active — le point d'accroche CSS pour supprimer le curseur natif via cursor: none — et l'élément émoji passe de display: none à display: block. Le mouseleave symétrique inverse les deux opérations : classe retirée, élément masqué. Le curseur système reprend sa place dès la sortie, sans délai.
Le suivi de position est direct et sans calcul intermédiaire. À chaque mousemove, le script écrit e.clientX dans style.left et e.clientY dans style.top de l'élément émoji. Pas de seuil, pas de force dégressive : l'émoji colle au pointeur pixel pour pixel dans le repère du viewport. Pour que l'écriture de ces coordonnées absolues produise le positionnement attendu, l'élément doit être en position: fixed dans le CSS — il se place alors au bon endroit quelle que soit la hauteur de défilement.
Le troisième écouteur, click, posé sur la zone, gère la rotation. Un index entier initialisé à 0 avance à chaque clic via l'opérateur modulo : (emojiIndex + 1) % emojis.length. Avec un tableau de cinq entrées — ✨ → 🚀 → 💫 → ⭐ → 🔥 → ✨… — la boucle ne s'arrête jamais. Le script met à jour textContent de l'élément avec l'entrée courante. L'index étant persistant entre deux entrées dans la zone (mais remis à 0 au rechargement), le visiteur retrouve le dernier émoji cliqué s'il revient survoler la zone.
Accessibilité
- L'élément
#cursorEmojiporte un caractère Unicode interprété par les lecteurs d'écran : « ✨ » sera annoncé « étincelles » selon la synthèse. Ajoutearia-hidden="true"sur cet élément avant tout déploiement — c'est un décor de pointeur, pas une information à vocaliser. - La rotation d'émoji se déclenche au
clicksur la zone, pas au clavier. Un utilisateur naviguant à la tabulation peut activer des éléments enfants dans la zone, mais il ne verra et ne déclenchera jamais le changement de symbole. Ne fais jamais dépendre une information ou un retour d'action de ce mécanisme. - Sur écran tactile, les trois écouteurs souris ne se déclenchent pas en usage normal. Le curseur émoji reste masqué, la zone se comporte comme un bloc ordinaire. Certains navigateurs mobiles émulent un
mouseenterau tap — teste ce comportement si la zone contient des éléments cliquables. - prefers-reduced-motion n'a pas d'impact direct ici. Si tu ajoutes une transition à l'apparition de l'émoji, cette préférence doit la gouverner — l'effet livré n'a rien à conditionner.
Compatibilité navigateur
La syntaxe JavaScript est ES2015 : fonctions fléchées, const, modulo sur variable — IE 11 s'arrête sur une erreur de syntaxe. Le vrai facteur de compatibilité est le moteur d'émojis de l'OS : Apple Color Emoji (macOS/iOS), Segoe UI Emoji (Windows) et Noto Color Emoji (Android/Linux) peuvent produire des dessins radicalement différents pour le même caractère Unicode.
Sans JavaScript, <code>#cursorEmoji</code> reste en <code>display: none</code> et le curseur natif fonctionne normalement. Sur un moteur ES5, une erreur de syntaxe bloque l'exécution ; la zone reste visible et lisible. L'effet est purement additif : rien ne casse sans lui.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
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éfaut | Effet |
|---|---|---|
| Le tableau d'émojis | ['✨', '🚀', '💫', '⭐', '🔥'] — 5 entrées | La seule liste de contenu du script. Le premier s'affiche à chaque entrée dans la zone, les suivants tournent dans l'ordre des clics. Le modulo s'adapte à n'importe quelle longueur — 1 entrée pour un curseur fixe, davantage pour un long cycle. |
| Émoji initial (emojiIndex = 0) | index 0 → '✨' | L'index est remis à 0 au rechargement, pas à chaque entrée dans la zone : si le visiteur clique, sort, puis revient, le curseur reprend sur le dernier émoji affiché. Pour démarrer sur un symbole autre que le premier du tableau, change la valeur initiale. |
| Décalage de positionnement | coin supérieur gauche de l'émoji = coordonnées exactes du curseur | L'émoji est positionné par son coin supérieur gauche : le curseur pointe dans l'angle du caractère, pas en son centre. Un décalage fixe de quelques pixels corrige ça sans toucher à la logique de positionnement. |
| Zone d'activation (#zone-emoji) | un div de 300 px de haut | Les écouteurs sont posés sur l'élément ciblé par getElementById('zone-emoji') — n'importe quel conteneur peut le remplacer. Pour couvrir toute la page, cible document.body et ajoute cursor: none sur body. |
| Logique d'affichage (display block / none) | basculement instantané au mouseenter / mouseleave | Basculement instantané, sans transition. Pour un fondu, passe à opacity avec une transition CSS. Ajoute pointer-events: none quand l'élément est invisible — sinon il intercepte les clics. |
| Classe cursor-active (sur #zone-emoji) | ajoutée au mouseenter, retirée au mouseleave | Cette classe porte cursor: none sur la zone. Pour rétablir le curseur natif sur un élément imbriqué (lien, champ), ajoute cursor: auto sur cet élément — il prime sur la règle du parent. |
FAQ
document.body à la place du div dédié, pose les écouteurs dessus, ajoute cursor: none sur body. L'élément #cursorEmoji doit rester en position: fixed pour se repositionner lors du défilement. Avec les écouteurs d'entrée/sortie sur body, l'émoji disparaît quand la souris sort de la fenêtre — comportement cohérent.style.left et style.top) sans calcul lourd — pas de Math.sqrt, pas de getBoundingClientRect(). Sur un élément en position: fixed hors flux, le navigateur traite cette mise à jour sans reflow de la page. Un requestAnimationFrame autour des écritures peut plafonner la fréquence si besoin — mais c'est une optimisation rarement nécessaire sur cet effet.