Interactive Mesh
Dégradé maillé interactif : trois taches de couleur floues suivent le curseur avec un éasing CSS, sur une couche statique radiale — pur DOM, sans canvas.
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. Backgrounds comprend 50 effets, dont 6 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
L'effet repose sur trois <div> (.interactive-mesh-blob) positionnées en absolute avec border-radius: 50% et filter: blur(40px). Ce filtre CSS transforme chaque disque coloré en tache lumineuse diffuse ; superposées, les trois taches fusionnent en un dégradé maillé dont les plages de couleur se chevauchent selon la position du pointeur.
Au mousemove, le script calcule la position du curseur dans le référentiel du conteneur, puis repositionne chaque blob via style.left et style.top — avec un décalage propre à chaque blob (dx, dy exprimés en fraction de la largeur/hauteur du conteneur). Chacun gravite autour du curseur à une distance distincte, créant une constellation de couleurs en mouvement permanent.
Le glissement fluide est entièrement assuré par la règle CSS transition: left .3s ease-out, top .3s ease-out — sans un seul requestAnimationFrame. Le navigateur interpole lui-même les positions : l'ease-out donne au suivi un caractère élastique, réactif au démarrage du geste et doux à l'arrivée.
Une quatrième couche statique, .interactive-mesh-static, est un double radial-gradient (ambre bas-gauche, vert haut-droite) avec filter: blur(25px), fixée en inset: 0. Elle ancre la palette et enrichit le fond même quand le curseur est en dehors du conteneur.
Accessibilité
- prefers-reduced-motion : non implémenté dans le code livré — les blobs se déplacent toujours lors du
mousemove. Pour les utilisateurs sensibles, ajoutez un guardif (matchMedia('(prefers-reduced-motion: reduce)').matches) return;avant d'attacher l'écouteur. - Aucun
aria-hiddennirole="img"n'est posé sur le conteneur. Ajoutezaria-hidden="true"si l'effet est purement décoratif, ourole="img" aria-label="…"s'il doit être annoncé par les lecteurs d'écran. - L'interaction repose uniquement sur
mousemove— aucune réponse clavier ni tactile n'est définie. Sur appareils tactiles sans pointeur (mobile), les blobs restent à leurs positions CSS initiales (dégradé fixe). - Le
span.interactive-mesh-label(texte « Bougez la souris ») est du texte DOM lu par les technologies d'assistance ; son contenu sera annoncé si le conteneur est dans le flux de lecture. filter: blurest rendu sur couche composée (GPU) dans Chrome, Firefox et Safari — pas d'impact notable sur l'accessibilité motrice, mais à surveiller sur appareils bas de gamme sous charge CPU élevée.
Compatibilité navigateur
Nécessite filter: blur() CSS et l'événement mousemove — supportés dans tous les navigateurs modernes. Zéro dépendance externe, zéro canvas.
Sans support <code>filter: blur</code> (navigateurs très anciens), les blobs s'affichent comme des disques de couleur solides — l'effet visuel diffère mais aucune erreur JS ne se produit. La couche <code>interactive-mesh-static</code> reste un fond cohérent.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="interactive-mesh" id="interactiveMesh">
<div class="interactive-mesh-blob"></div>
<div class="interactive-mesh-blob"></div>
<div class="interactive-mesh-blob"></div>
<div class="interactive-mesh-static"></div>
<span class="interactive-mesh-label">Bougez la souris</span>
<!-- Optionnel : overlay texte/CTA -->
</div>
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 |
|---|---|---|
| .interactive-mesh-blob:nth-child(1) background | rgba(255, 0, 128, .6) | Couleur et opacité de la première tache (magenta). L'opacité (0.4–0.7) contrôle l'intensité de la fusion avec les autres blobs. |
| .interactive-mesh-blob:nth-child(2) background | rgba(0, 200, 255, .5) | Couleur de la deuxième tache (cyan). Jouer chaud vs froid entre les blobs crée une impression de profondeur. |
| .interactive-mesh-blob:nth-child(3) background | rgba(128, 0, 255, .55) | Couleur de la troisième tache (violet). Pour ajouter un blob, dupliquez un .interactive-mesh-blob et ajoutez son offset {dx, dy} dans le tableau JS. |
| .interactive-mesh-blob filter | blur(40px) | Intensité du flou de chaque tache. 25 px = taches nettes et saturées, 60 px = dégradé ultra-diffus. Les valeurs élevées augmentent légèrement le coût GPU. |
| .interactive-mesh-blob transition | left .3s ease-out, top .3s ease-out | Durée et courbe de l'éasing. 0.15s = suivi immédiat (type ombre portée), 0.6s ease-in-out = flottement paresseux. |
| offsets (JS, tableau ligne 3) | [{dx:-0.3,dy:-0.2},{dx:0.1,dy:0.15},{dx:0.25,dy:-0.1}] | Décalage de chaque blob autour du curseur, en fraction de la largeur/hauteur du conteneur. Réduisez les valeurs absolues pour un maillage concentré, augmentez pour une dispersion dramatique. |
| .interactive-mesh-static background | radial-gradient(at 10% 80%, rgba(255,200,0,.3) 0, transparent 50%), radial-gradient(at 90% 20%, rgba(0,255,128,.25) 0, transparent 50%) | Couche statique toujours visible. Modifiez les positions (ex. at 10% 80%) et couleurs pour ancrer une teinte de fond même quand le curseur est absent. |
FAQ
<div> blob sont repositionnées avec style.left et style.top à chaque mousemove, et la règle transition: left .3s ease-out délègue l'interpolation au moteur CSS du navigateur. Le résultat est aussi fluide qu'une animation RAF, sans aucune boucle JS.getElementById('interactiveMesh') — un seul élément. Pour plusieurs instances, extrayez la logique dans une fonction initMesh(el) qui reçoit le conteneur en argument, puis appelez-la une fois par élément cible.mousemove, non déclenché sur les interfaces tactiles pures. Sur mobile, les blobs restent à leurs positions CSS initiales (dégradé statique). Ajoutez un écouteur touchmove pour cibler le tactile : récupérez e.touches[0].clientX/Y à la place de e.clientX/Y.