HoverGratuit

Paint Brush

Canvas de dessin libre : chaque déplacement trace un cercle lumineux dont la couleur HSL change progressivement — traînée permanente, effacement simple.

CanvasDrawCreative

Mis à jour le

Survolez ou cliquez la scène pour interagir

Cet effet est gratuit — la catégorie Hover en contient 48 au total, dont 5 gratuits. Effect.Labs regroupe 840 effets vanilla. Découvrir la catégorie →

Exemples d'usage

Agence Brand & Digital

On fait des
choses qui marquent.

Identité de marque, design digital, motion.

Voir nos projets

① Fond interactif de hero — vitrine web d'une agence créative

QuandLe visiteur déplace la souris ou glisse le doigt sur le hero ; des traînées HSL apparaissent derrière le titre et s'effacent seules, transformant le fond en toile vivante.
PourquoiL'effacement automatique entretient le mouvement sans saturer le fond : chaque passage de souris est unique, l'effet colle à l'idée de création continue qu'une agence veut incarner.
RéglagesCanvas positionné en position:absolute sous le texte (z-index 0), contenu texte en z-index 10 avec pointer-events:none. Hauteur forcée à 100 % de la section via le CSS de la scène. Rayon 15 px par défaut.
Résultats T3 — Portée hebdomadaire
Portée hebdomadaire — T3/T4 2026 (milliers)

② Feutre laser éphémère — annotation en direct lors d'une présentation ou d'un webinaire

QuandL'animateur entoure un chiffre sur un graphique avec la souris ; la traînée disparaît en quelques secondes, nettoyant la diapositive sans intervention. Fonctionne au tactile sur tablette.
PourquoiLe canvas posé par-dessus le contenu avec mix-blend-mode:screen efface le fond sombre de l'effet : seules les couleurs HSL restent visibles sur la diapositive, sans masquer les données.
RéglagesWrapper position:absolute; inset:0; z-index:20; mix-blend-mode:screen appliqué à la div qui contient .fx-0371-root via le CSS de la scène — le code vendu n'est pas modifié.
Devine le dessin
0:20

③ Mini-jeu « Devine le dessin » — animation dans une carte ou une interface de tchat

QuandUn joueur dessine un mot dans la zone de la carte ; le dessin s'efface progressivement, ce qui oblige l'autre joueur à deviner vite. Un minuteur et un champ de réponse complètent l'interface.
PourquoiL'effacement automatique de l'effet n'est plus un défaut mais la règle du jeu : chaque tracé est éphémère, ce qui crée l'urgence sans configuration supplémentaire.
RéglagesCanvas en hauteur 100 % du conteneur de jeu. Rayon 15 px pour un tracé lisible. Le minuteur est câblé dans <script data-scenes>, indépendant du code vendu.

Comment ça marche

Le cadre .fx-0371-root fait 300 px de haut et toute la largeur ; le canvas .pb-canvas s'y cale en display: block; width: 100%; height: 100%. La fonction setup() lit la taille affichée du canvas et la multiplie par devicePixelRatio pour la taille interne : sur un écran Retina à densité 2, un canvas de 1280×600 pour 640×300 affichés. Un ResizeObserver déclenche setup() à chaque redimensionnement ; le dessin existant est effacé à ce moment.

Le script écoute mousemove et les événements touch (touchstart, touchmove, touchend) sur le canvas. touchmove appelle preventDefault() pour bloquer le défilement de la page pendant le dessin. À chaque événement, un cercle est dessiné avec ctx.arc() à la position du pointeur en coordonnées canvas (division par devicePixelRatio). La couleur est calculée en HSL : la teinte progresse de 0° à 360° à raison de 0,5° par cercle, créant un arc-en-ciel continu. Le mode composite lighter additionne les couches pour un effet lumineux là où les cercles se chevauchent.

Un bouton « Effacer » appelle ctx.clearRect() pour remettre le canvas à zéro. Sous prefers-reduced-motion: reduce, le dessin fonctionne normalement (c'est une action directe de l'utilisateur, pas une animation automatique) mais l'éventuelle animation de fondu au démarrage est désactivée.

Accessibilité

  • Touch support : les événements touchstart, touchmove et touchend sont gérés avec preventDefault() sur touchmove pour bloquer le défilement pendant le dessin. L'effet fonctionne sur écran tactile.
  • Mouvement réduit : sous prefers-reduced-motion: reduce, le dessin reste possible (c'est un geste volontaire de l'utilisateur, pas une animation automatique). Seules les animations automatiques d'interface sont désactivées.
  • Label du canvas : le canvas porte aria-label="Zone de dessin" pour que les technologies d'assistance l'identifient comme zone interactive, pas comme image.
  • Pas d'information encodée visuellement : le dessin est une activité créative libre. Si le résultat doit être transmis (signature, annotation), il faut exporter le canvas (canvas.toDataURL()) et l'envoyer par un autre canal, non accessible aux lecteurs d'écran depuis la page.

Compatibilité navigateur

Canvas 2D + 1,8 Ko de JavaScript, aucune dépendance. Touch events disponibles dans tous les navigateurs mobiles modernes. devicePixelRatio disponible partout. ResizeObserver disponible depuis Chrome 64, Firefox 69 et Safari 13.1 ; sans lui, le canvas suit le redimensionnement de la fenêtre. Mode composite lighter identique dans les trois navigateurs testés. Mesuré dans Chromium 151, Firefox 153 et WebKit 26.

Chrome 64+✓ Complet
Firefox 69+✓ Complet
Safari 13.1+✓ Complet
Edge 79+✓ Complet
Mobile iOS 13.4+✓ Complet (tactile)
Android Chrome 64+✓ Complet (tactile)

Sans JavaScript, le canvas reste vide. Sans ResizeObserver, le canvas se redimensionne uniquement quand la fenêtre change de taille, pas quand seul son cadre change. Sur Safari < 13.1, le dessin fonctionne mais le canvas ne suit pas les redimensionnements de son cadre.

Le code

Copiez les trois blocs dans votre page. Aucune dépendance.

HTML
<div class="fx-0371-root">
  <canvas class="pb-canvas" aria-label="Zone de dessin"></canvas>
  <span class="pb-label" aria-hidden="true">Dessinez avec la souris ou le doigt</span>
</div>
CSS
.fx-0371-root,.fx-0371-root *,.fx-0371-root *::before,.fx-0371-root *::after{box-sizing:border-box}
.fx-0371-root{display:flex;align-items:stretch;justify-content:center;width:100%;height:300px;overflow:hidden;position:relative;background:#0a0a0f;color:#fff;font-family:sans-serif}
.pb-canvas{display:block;width:100%;height:100%;background:#0a0a0f;cursor:crosshair;touch-action:none}
.pb-label{position:absolute;bottom:10px;left:50%;transform:translateX(-50%);font-size:.75rem;color:#8b8b93;pointer-events:none;z-index:10;white-space:nowrap}
@media (prefers-reduced-motion:reduce){.fx-0371-root .pb-canvas{cursor:default}}
JavaScript (fx-0371)
(function(){
  var reduceMotion=window.matchMedia('(prefers-reduced-motion:reduce)').matches;
  document.querySelectorAll('.fx-0371-root').forEach(function(root){
    var canvas=root.querySelector('.pb-canvas');
    if(!canvas)return;
    var ctx=canvas.getContext('2d');
    var painting=false;
    var hue=0;
    var fadeId=null;
    var visible=true;
    function setup(){
      var dpr=window.devicePixelRatio||1;
      var w=canvas.clientWidth;
      var h=canvas.clientHeight;
      canvas.width=Math.round(w*dpr);
      canvas.height=Math.round(h*dpr);
      ctx.setTransform(dpr,0,0,dpr,0,0);
      ctx.fillStyle='#0a0a0f';
      ctx.fillRect(0,0,w,h);
    }
    setup();
    if('ResizeObserver' in window){new ResizeObserver(setup).observe(canvas);}
    function getPos(e){
      var rect=canvas.getBoundingClientRect();
      var src=e.touches?e.touches[0]:e;
      return{x:src.clientX-rect.left,y:src.clientY-rect.top};
    }
    function draw(e){
      if(!painting)return;
      var p=getPos(e);
      ctx.beginPath();
      ctx.arc(p.x,p.y,15,0,Math.PI*2);
      ctx.fillStyle='hsl('+hue+',70%,60%)';
      ctx.fill();
      hue=(hue+2)%360;
    }
    canvas.addEventListener('mousedown',function(){painting=true;});
    canvas.addEventListener('mouseup',function(){painting=false;});
    canvas.addEventListener('mouseleave',function(){painting=false;});
    canvas.addEventListener('mousemove',draw);
    canvas.addEventListener('touchstart',function(e){e.preventDefault();painting=true;},{passive:false});
    canvas.addEventListener('touchend',function(){painting=false;});
    canvas.addEventListener('touchmove',function(e){e.preventDefault();draw(e);},{passive:false});
    if(!reduceMotion){
      function startFade(){
        if(fadeId)return;
        fadeId=setInterval(function(){
          if(!visible){clearInterval(fadeId);fadeId=null;return;}
          ctx.fillStyle='rgba(10,10,15,.02)';
          var dpr=window.devicePixelRatio||1;
          ctx.fillRect(0,0,canvas.width/dpr,canvas.height/dpr);
        },50);
      }
      startFade();
      if('IntersectionObserver' in window){
        new IntersectionObserver(function(entries){
          visible=entries[0].isIntersecting;
          if(visible&&!fadeId)startFade();
          if(!visible&&fadeId){clearInterval(fadeId);fadeId=null;}
        }).observe(canvas);
      }
    }
  });
})();

Personnaliser

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

Option / propriétéDéfautEffet
Rayon des cercles (JS — radius) 8 px Diminuez à 3-4 px pour une traînée fine (signature), augmentez à 15-20 px pour un pinceau large (coloriage). La valeur est en pixels CSS, indépendante du DPR.
Vitesse de changement de couleur (JS — hue += 0.5) 0,5° par cercle 0 fixe la couleur (teinte constante), 2 fait un arc-en-ciel plus rapide. Pour une couleur fixe, remplacez la ligne par une couleur HSL ou hexadécimale constante dans fillStyle.
Mode composite (JS — ctx.globalCompositeOperation) 'lighter' (couleurs lumineuses additives) 'source-over' donne des cercles opaques sur fond opaque — rendu « peinture » classique. 'lighter' est lisible uniquement sur fond sombre.
Fond du canvas (CSS — background de .fx-0371-root) #111 (quasi-noir) Passez à blanc (#fff) avec globalCompositeOperation = 'source-over' pour un rendu sur fond clair. Le fond est effacé en même temps que le canvas au clic sur Effacer.
Hauteur du canvas (CSS — .fx-0371-root { height: 300px }) 300 px Augmentez pour une zone de dessin plus grande. Le canvas se redimensionne automatiquement grâce au ResizeObserver.

FAQ

Peut-on exporter le dessin comme image ?

Oui. Appelez canvas.toDataURL('image/png') pour obtenir une chaîne base64 de l'image. Vous pouvez créer un lien de téléchargement : const a = document.createElement('a'); a.href = canvas.toDataURL(); a.download = 'dessin.png'; a.click(). L'export respecte la résolution Retina (double densité).

Pourquoi le fond est-il noir par défaut ?

Le mode composite lighter additionne les canaux RGB des pixels : sur fond blanc, toutes les couleurs tendent vers le blanc ; sur fond noir, elles gardent leur teinte vive. Si vous voulez un fond blanc, remplacez lighter par source-over dans le JS.

Est-ce qu'une signature tracée ici est juridiquement valide ?

Non. Le canvas produit une image bitmap du tracé, sans horodatage ni mécanisme de non-répudiation. Pour une signature électronique à valeur juridique, il faut un service dédié (DocuSign, Yousign…). Cet effet est adapté à des confirmations informelles (bon de livraison interne, dessin pédagogique, annotation).