3DGratuit

Floating Shadows

Trois pastilles (◆ ★ ♥) posées au-dessus de leurs ombres de contact : le pointeur joue la lampe, les ombres au sol glissent à l'opposé et s'étirent, l'ombre portée des pastilles tourne et elles se soulèvent un peu — un même éclairage pour les trois, à la souris, au stylet ou au doigt ; si l'utilisateur réduit les animations, les pastilles ne bougent plus et seules les ombres suivent la lumière.

JSMouseShadow

Mis à jour le

♦
★
♥
Survolez ou cliquez la scène pour interagir

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

Exemples d'usage

Où planter les hostas à l'ombre ?

Jardins Lemaire · paysagiste

Déplacez le soleil avec la souris : les ombres tournent au pied des arbustes.

① Ombre de trois arbustes selon la course du soleil — outil d'un paysagiste

QuandSur la page « Où planter les hostas à l'ombre ? », trois arbustes ronds sont posés sur une pelouse ; la souris joue le soleil et les ombres tournent au pied des arbustes, une consigne en bas de la scène.
PourquoiLe pointeur-lumière rend concret un raisonnement d'ombre portée qu'un plan figé n'explique pas. Un éclairage commun aux trois, comme celui du soleil, donne des ombres parallèles : c'est juste pour des arbustes en plein jardin.
RéglagesRacine et conteneur étendus à toute la scène ; pastilles rondes en dégradé vert (78 px, l'arbuste central à 96 px), symboles masqués (font-size: 0) ; ombres au sol vert sombre de 74 × 16 px ; titre et consigne posés en absolu dans le conteneur.

Atelier Roussel architectes

Étude d'ombres, parcelle des Tilleuls

MaisonAtelierAbri vélos

② Maquettes d'architecte sous la lampe — étude d'ombres d'un cabinet d'architecture

QuandLa page de l'étude d'ombres de la parcelle des Tilleuls montre trois petits volumes blancs, maison, atelier et abri vélos, sur une table grise ; la souris déplace la lampe et les ombres glissent sous les volumes.
PourquoiLes ombres de contact qui s'allongent et s'éloignent du pointeur suffisent à faire lire des volumes posés sur une table, sans rendu 3D.
RéglagesPastilles blanches à coins de 2 px, de tailles différentes (84 × 64, 104 × 70, 52 × 44 px), toit en triangle de bordures sur la maison (::before) ; fond gris clair ; ombres gris ardoise ; légende des trois volumes en bas.
Atelier Pinceau FinFigurines peintes à la main, sur commande

Cavalier, tour, fou : série 28 mmDélai 3 semaines

③ Figurines peintes sous la lampe d'atelier — boutique d'un peintre de figurines

QuandLa boutique présente trois socles ronds (cavalier, tour, fou) sur un plan de travail en bois ; la lampe suit la souris et les ombres tournent sous les socles.
PourquoiMontrer une pièce sous une lumière qui bouge est le geste du peintre qui inspecte son travail ; ici, c'est le visiteur qui tient la lampe.
RéglagesPastilles rondes anthracite de 78 px portant les symboles ♞ ♜ ♝ en 40 px, chacun dans sa teinte de peinture ; ombres au sol brun très sombre ; plan de travail en dégradé bois avec lames ; nom de l'atelier en haut, série et délai en bas.

Comment ça marche

La racine .fx-0029-root (250 px de haut, fond rgb(26, 26, 36)) contient .float-shadow-container, marqué data-float-shadow : toute la largeur et 250 px de haut, qui aligne au centre trois .float-shadow-el espacés de 30 px. Chacun porte une pastille .float-shadow-shape de 60 × 60 px (carré arrondi indigo, rond rose, carré arrondi vert, en dégradés) et, dessous, une ombre de contact .float-shadow-shadow : une ellipse de 50 × 10 px en dégradé radial noir à 35 %, centrée 15 px sous la pastille.

Le script parcourt toutes les racines .fx-0029-root de la page et branche une instance par groupe. À chaque mouvement du pointeur, il calcule la position de la lumière par rapport au centre du conteneur, de −1 à +1 sur chaque axe, puis applique le même éclairage aux trois éléments : l'ombre au sol glisse à l'opposé du pointeur (jusqu'à 15 px) et s'étire en largeur quand la lumière monte ou descend (jusqu'à ×1,3) en pâlissant ; la pastille reçoit une ombre portée de 20 px de flou décalée à l'opposé de la lumière, se décale de 3 px vers elle et se soulève jusqu'à 5 px. Mesuré dans Chromium 151, Firefox 153 et WebKit 26.5 avec le pointeur au bord droit : 13,5 px de glissement des ombres au sol. Quand le pointeur sort, tout revient à l'état de repos ; les ombres au sol suivent avec une transition de 0,3 s.

Le décalage est calculé une fois, depuis le centre du conteneur, et vaut pour les trois pastilles : elles réagissent comme sous un soleil lointain, toutes leurs ombres parallèles, et non comme sous une lampe placée entre elles, qui donnerait une direction d'ombre par objet. Le script écoute les événements pointer : au doigt, touch-action: pan-y garde le défilement vertical de la page, et un glissé de côté déplace la lumière ; tout revient au repos quand le doigt se lève.

Accessibilité

  • Mouvement réduit : sous prefers-reduced-motion: reduce, un bloc limité à .fx-0029-root impose transform: none aux pastilles : elles ne se soulèvent plus et ne se décalent plus. Les ombres continuent de suivre la lumière, ce qui garde l'information « d'où vient la lumière » sans déplacer les objets. Mesuré dans Chromium, Firefox et WebKit : pastilles immobiles sous la souris, ombres au sol toujours décalées.
  • Décor : rien n'est à activer et rien n'est à atteindre au clavier ; l'état de repos, trois pastilles posées sur leurs ombres, se lit tel quel. Si les pastilles représentent des objets qui comptent (des produits, des bâtiments), nommez-les dans le texte, comme les légendes des scènes.
  • Lecteurs d'écran : les symboles ◆ ★ ♥ sont lus (« losange noir », « étoile noire », « cœur noir ») : ajoutez aria-hidden="true" sur les pastilles si elles sont décoratives, comme dans les scènes.
  • Contraste : symboles blancs sur indigo #6366f1 à 4,5:1 et sur rose #ec4899 à 3,5:1 ; sur le vert #10b981, seulement 2,5:1 : décoratif, pas pour du texte.

Compatibilité navigateur

CSS (transform, box-shadow, dégradés radiaux, transition, touch-action) + JS d'événements pointer, aucune dépendance. Mesuré dans Chromium 151, Firefox 153 et WebKit 26.5 : mêmes décalages, deux conteneurs indépendants sur une page, pastilles immobiles sous mouvement réduit ; glissé au doigt mesuré dans Chromium en mode tactile. Aucune 3D CSS : le rendu WebKit est fiable, un vrai Safari et un vrai iPhone n'ont pas été vérifiés.

Chrome 55+✓ Complet
Firefox 59+✓ Complet
Safari 13+✓ Mesuré sur WebKit
Edge 79+✓ Complet
Mobile iOS 13+✓ Non vérifié sur un vrai iPhone
Android Chrome✓ Complet, glissé de côté

Sans JavaScript, les trois pastilles restent posées sur leurs ombres de contact, à l'état de repos. Sur un écran tactile, un simple appui ne déplace pas la lumière : il faut glisser le doigt de côté.

Le code

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

HTML
<div class="fx-0029-root">
  <div class="float-shadow-container" data-float-shadow>
    <div class="float-shadow-el">
      <div class="float-shadow-shape fs-shape-1">&#9830;</div>
      <div class="float-shadow-shadow"></div>
    </div>
    <div class="float-shadow-el">
      <div class="float-shadow-shape fs-shape-2">&#9733;</div>
      <div class="float-shadow-shadow"></div>
    </div>
    <div class="float-shadow-el">
      <div class="float-shadow-shape fs-shape-3">&#9829;</div>
      <div class="float-shadow-shadow"></div>
    </div>
  </div>
</div>
CSS
.fx-0029-root,
.fx-0029-root *,
.fx-0029-root *::before,
.fx-0029-root *::after {
  box-sizing: border-box
}

.fx-0029-root {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 250px;
  overflow: hidden;
  position: relative;
  background: rgb(26, 26, 36);
  color: rgb(255, 255, 255);
  font-family: sans-serif;
}

.float-shadow-container {
  width: 100%;
  height: 250px;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 30px;
  cursor: crosshair;
  /* Touch: a sideways drag moves the light, a vertical swipe still scrolls the page */
  touch-action: pan-y;
}

.float-shadow-el {
  position: relative;
  transition: transform 0.3s ease;
}

.float-shadow-shape {
  width: 60px;
  height: 60px;
  border-radius: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  position: relative;
  z-index: 2;
}

.fs-shape-1 {
  background: linear-gradient(135deg, #6366f1, #818cf8);
}

.fs-shape-2 {
  background: linear-gradient(135deg, #ec4899, #f472b6);
  border-radius: 50%;
}

.fs-shape-3 {
  background: linear-gradient(135deg, #10b981, #34d399);
}

.float-shadow-shadow {
  position: absolute;
  bottom: -15px;
  left: 50%;
  transform: translateX(-50%);
  width: 50px;
  height: 10px;
  background: radial-gradient(ellipse, rgba(0, 0, 0, 0.35) 0%, transparent 70%);
  border-radius: 50%;
  transition: all 0.3s ease;
  z-index: 1;
}

/* Reduced motion: the shapes no longer lift; only the shadows follow the light */
@media (prefers-reduced-motion: reduce) {
  .fx-0029-root .float-shadow-shape {
    transform: none !important;
  }
}
JavaScript (fx-0029)
(function() {
  // ==========================================
  // 9. Floating Shadows - Dynamic light source (pointer events: mouse, pen and touch)
  // One instance per .fx-0029-root: several groups on one page each follow their own light.
  // ==========================================
  document.querySelectorAll('.fx-0029-root').forEach(function(root) {
    var container = root.querySelector('[data-float-shadow]');
    if (!container) return;
    var elements = container.querySelectorAll('.float-shadow-el');
    container.addEventListener('pointermove', function(e) {
      var rect = container.getBoundingClientRect();
      var mx = e.clientX - rect.left;
      var my = e.clientY - rect.top;
      var centerX = rect.width / 2;
      var centerY = rect.height / 2;
      var lightX = (mx - centerX) / centerX;
      var lightY = (my - centerY) / centerY;

      elements.forEach(function(el) {
        var shape = el.querySelector('.float-shadow-shape');
        var shadow = el.querySelector('.float-shadow-shadow');
        // Move shadow opposite to light direction
        var shadowX = -lightX * 15;
        var shadowY = lightY * 5 + 5;
        var shadowScale = 1 + Math.abs(lightY) * 0.3;
        shadow.style.transform = 'translateX(calc(-50% + ' + shadowX + 'px)) scaleX(' + shadowScale + ')';
        shadow.style.opacity = 0.6 - Math.abs(lightY) * 0.2;
        // Lift shape slightly toward light
        shape.style.transform = 'translate(' + (lightX * 3) + 'px, ' + (-Math.abs(lightY) * 5) + 'px)';
        // Dynamic box shadow on shape
        shape.style.boxShadow = ((-lightX * 8)) + 'px ' + ((-lightY * 8 + 4)) + 'px 20px rgba(0,0,0,0.3)';
      });
    });
    container.addEventListener('pointerleave', function() {
      elements.forEach(function(el) {
        var shape = el.querySelector('.float-shadow-shape');
        var shadow = el.querySelector('.float-shadow-shadow');
        shadow.style.transform = 'translateX(-50%)';
        shadow.style.opacity = '0.35';
        shape.style.transform = 'translate(0, 0)';
        shape.style.boxShadow = '';
      });
    });
  });
})();

Personnaliser

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

Option / propriétéDéfautEffet
Glissement des ombres au sol (JS — -lightX * 15) 15 px au plus 30 donne des ombres longues de fin de journée ; 8, une lumière presque au zénith.
Étirement des ombres (JS — Math.abs(lightY) * 0.3) jusqu'à ×1,3 Allonge l'ombre quand la lumière est basse ou haute. 0.6 l'étire nettement ; 0 la garde ronde.
Ombre portée des pastilles (JS — boxShadow, facteur 8 et flou 20px) décalage de 8 px, flou de 20 px, noir à 30 % Un facteur de 14 détache franchement les pastilles du fond ; un flou de 10 px donne une lumière plus dure.
Soulèvement (JS — lightX * 3 et Math.abs(lightY) * 5) 3 px de côté, 5 px vers le haut Mettez 0 et 0 pour des objets posés, qui ne bougent jamais : seules les ombres réagissent.
Pastilles (CSS — .float-shadow-shape, .fs-shape-1 à -3) 60 × 60 px, dégradés indigo, rose, vert Taille, couleur et forme libres. Évitez clip-path sur la pastille : il couperait son ombre portée ; dessinez la forme avec border-radius ou un ::before, comme le toit de la deuxième scène.
Ombres au sol (CSS — .float-shadow-shadow) 50 × 10 px, noir à 35 %, 15 px sous la pastille Élargissez-les avec les pastilles (74 × 16 px pour des pastilles de 78 px) et teintez-les selon le sol (vert sombre sur une pelouse).

FAQ

Les trois ombres bougent ensemble : peut-on leur donner une direction chacune ?

Dans ce code, non : la lumière est calculée une fois, depuis le centre du conteneur, et le même décalage vaut pour les trois, comme sous un soleil lointain. Pour une lampe proche, il faudrait calculer lightX et lightY dans la boucle, depuis le centre de chaque élément (el.getBoundingClientRect()), à la place du centre du conteneur.

Pourquoi ma forme découpée perd-elle son ombre ?

L'ombre qui tourne est un box-shadow sur la pastille ; un clip-path ou un mask la coupe avec la forme. Dessinez la forme avec border-radius, des bordures ou un pseudo-élément, ou remplacez le box-shadow par filter: drop-shadow(…) sur un élément parent.

Puis-je mettre plusieurs groupes sur une page ?

Oui. Chaque groupe a sa racine .fx-0029-root et le script donne à chacun son instance : chaque groupe ne réagit qu'au pointeur qui le survole. C'est le cas sur cette page, où la démo du haut et les trois exemples tournent avec un seul script.