Blog / JavaScript

Elements interactifs en JavaScript : drag, magnetic et morph

Apprenez a creer des elements interactifs engageants qui reagissent au curseur : drag and drop, effets magnetiques, animations elastiques et morphing de formes. Des techniques qui transforment vos interfaces.

Introduction aux elements interactifs

Les interfaces modernes ne se contentent plus d'afficher du contenu statique. Les utilisateurs s'attendent a des interactions fluides et engageantes qui repondent a leurs actions de maniere naturelle et intuitive.

Dans ce guide complet, nous allons explorer 6 techniques essentielles pour creer des elements interactifs en JavaScript pur : du drag and drop natif aux effets magnetiques, en passant par les animations elastiques et le morphing de formes.

💡
Bon a savoir

Toutes ces techniques fonctionnent en JavaScript vanilla, sans dependance externe. Elles utilisent principalement les evenements mousedown, mousemove et mouseup combines avec requestAnimationFrame pour des animations fluides.

Chaque exemple est accompagne d'une demo interactive et du code complet que vous pouvez copier et adapter a vos projets. Les techniques presentees sont compatibles avec tous les navigateurs modernes.

1. Drag and Drop natif

Le drag and drop est l'une des interactions les plus intuitives. Permettre a l'utilisateur de deplacer des elements a la souris cree une experience tactile et engageante, meme sur desktop.

Voici une implementation simple mais efficace qui gere correctement les limites du conteneur :

Drag me
draggable.js
function makeDraggable(element, container) {
  let isDragging = false;
  let startX, startY, initialX, initialY;

  element.addEventListener('mousedown', (e) => {
    isDragging = true;
    startX = e.clientX;
    startY = e.clientY;
    initialX = element.offsetLeft;
    initialY = element.offsetTop;
    element.classList.add('dragging');
  });

  document.addEventListener('mousemove', (e) => {
    if (!isDragging) return;

    const dx = e.clientX - startX;
    const dy = e.clientY - startY;

    // Calcul des limites
    const maxX = container.offsetWidth - element.offsetWidth;
    const maxY = container.offsetHeight - element.offsetHeight;

    let newX = Math.max(0, Math.min(maxX, initialX + dx));
    let newY = Math.max(0, Math.min(maxY, initialY + dy));

    element.style.left = newX + 'px';
    element.style.top = newY + 'px';
  });

  document.addEventListener('mouseup', () => {
    isDragging = false;
    element.classList.remove('dragging');
  });
}

Points cles de l'implementation

  • Calcul des offsets : On stocke la position initiale au mousedown pour calculer le deplacement relatif
  • Limites du conteneur : Math.max et Math.min garantissent que l'element reste dans sa zone
  • Ecoute sur document : mousemove et mouseup sur document pour eviter de perdre le drag si le curseur sort de l'element

2. Elements magnetiques

L'effet magnetique attire un element vers le curseur quand celui-ci s'en approche. C'est une technique tres populaire pour les boutons CTA car elle cree une sensation d'interactivite unique.

magnetic.js
function makeMagnetic(element, zone, strength = 0.35, radius = 120) {
  zone.addEventListener('mousemove', (e) => {
    const rect = element.getBoundingClientRect();
    const centerX = rect.left + rect.width / 2;
    const centerY = rect.top + rect.height / 2;

    const deltaX = e.clientX - centerX;
    const deltaY = e.clientY - centerY;
    const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);

    if (distance < radius) {
      // Plus le curseur est proche, plus l'attraction est forte
      const factor = 1 - (distance / radius);
      const moveX = deltaX * strength * factor;
      const moveY = deltaY * strength * factor;

      element.style.transform = `translate(${moveX}px, ${moveY}px)`;
    } else {
      element.style.transform = 'translate(0, 0)';
    }
  });

  zone.addEventListener('mouseleave', () => {
    element.style.transform = 'translate(0, 0)';
  });
}
🎯
Parametres ajustables

strength (0.2-0.5) controle l'intensite du deplacement. radius (80-150px) definit la zone d'activation. Experimentez pour trouver le bon equilibre entre effet spectaculaire et utilisabilite.

3. Effet elastique au drag

L'effet elastique ajoute une physique realiste au drag and drop : l'element suit le curseur avec un leger retard et un effet de rebond, comme s'il etait attache par un elastique.

Elastic
elastic-drag.js
function makeElasticDrag(element, container) {
  let isDragging = false;
  let targetX = 0, targetY = 0;
  let currentX = 0, currentY = 0;
  let velocityX = 0, velocityY = 0;

  const spring = 0.15;  // Force du ressort
  const friction = 0.75; // Amortissement

  function animate() {
    // Physique du ressort
    const dx = targetX - currentX;
    const dy = targetY - currentY;

    velocityX += dx * spring;
    velocityY += dy * spring;

    velocityX *= friction;
    velocityY *= friction;

    currentX += velocityX;
    currentY += velocityY;

    element.style.transform = `translate(${currentX}px, ${currentY}px)`;

    requestAnimationFrame(animate);
  }

  animate();

  let offsetX, offsetY;

  element.addEventListener('mousedown', (e) => {
    isDragging = true;
    const rect = container.getBoundingClientRect();
    offsetX = e.clientX - rect.left - currentX;
    offsetY = e.clientY - rect.top - currentY;
  });

  document.addEventListener('mousemove', (e) => {
    if (!isDragging) return;
    const rect = container.getBoundingClientRect();
    targetX = e.clientX - rect.left - offsetX;
    targetY = e.clientY - rect.top - offsetY;
  });

  document.addEventListener('mouseup', () => {
    isDragging = false;
  });
}

La physique du ressort

Cette technique utilise un modele de spring physics simplifie :

  • spring (0.1-0.3) : Plus la valeur est elevee, plus l'element suit rapidement
  • friction (0.7-0.9) : Controle l'amortissement, une valeur basse = plus de rebonds
  • requestAnimationFrame : Assure une animation a 60fps pour une fluidite optimale

4. Rotation 3D suivant le curseur

L'effet de rotation 3D (ou "tilt effect") fait pivoter un element en fonction de la position du curseur, creant une illusion de profondeur tres immersive.

Bougez la souris
tilt-3d.js
function makeTiltable(element, maxAngle = 20) {
  element.addEventListener('mousemove', (e) => {
    const rect = element.getBoundingClientRect();

    // Position relative du curseur (-0.5 a 0.5)
    const x = (e.clientX - rect.left) / rect.width - 0.5;
    const y = (e.clientY - rect.top) / rect.height - 0.5;

    // Rotation inversee pour un effet naturel
    const rotateY = x * maxAngle;
    const rotateX = -y * maxAngle;

    element.style.transform = `
      perspective(1000px)
      rotateX(${rotateX}deg)
      rotateY(${rotateY}deg)
      scale3d(1.05, 1.05, 1.05)
    `;
  });

  element.addEventListener('mouseleave', () => {
    element.style.transform = 'perspective(1000px) rotateX(0) rotateY(0)';
  });
}
⚠️
Performance

Les transformations 3D sont accelerees par le GPU, mais evitez d'appliquer cet effet a trop d'elements simultanement. Pour les listes longues, activez l'effet uniquement sur l'element survole.

5. Morph de formes au hover

Le morphing de formes permet de transformer fluidement un element d'une forme a une autre. Combine avec clip-path ou border-radius, vous pouvez creer des transitions organiques fascinantes.

Cliquez sur les formes ci-dessous pour les voir se transformer :

morph.css
.morph-shape {
  width: 100px;
  height: 100px;
  background: linear-gradient(135deg, #6366f1, #8b5cf6);
  cursor: pointer;
  transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.morph-shape.circle {
  border-radius: 50%;
}

.morph-shape.square {
  border-radius: 16px;
}

.morph-shape.rounded {
  border-radius: 30%;
}

.morph-shape.blob {
  border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
}

/* Animation continue pour le blob */
@keyframes blobMorph {
  0%, 100% {
    border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
  }
  50% {
    border-radius: 30% 60% 70% 40% / 50% 60% 30% 60%;
  }
}
morph.js
const shapes = ['circle', 'square', 'rounded', 'blob'];

function makeMorphable(element) {
  let currentIndex = shapes.findIndex(
    s => element.classList.contains(s)
  );

  element.addEventListener('click', () => {
    // Retirer la classe actuelle
    element.classList.remove(shapes[currentIndex]);

    // Passer a la forme suivante
    currentIndex = (currentIndex + 1) % shapes.length;

    // Ajouter la nouvelle classe
    element.classList.add(shapes[currentIndex]);
  });
}

6. Combinaisons creatives

La vraie magie arrive quand vous combinez plusieurs effets. Voici un element qui integre drag, rotation 3D et morphing au survol :

Drag + Tilt + Morph
combo-interactive.js
function makeComboInteractive(element, container) {
  let isDragging = false;
  let posX = 0, posY = 0;
  let rotateX = 0, rotateY = 0;

  function updateTransform() {
    element.style.transform = `
      translate(${posX}px, ${posY}px)
      perspective(800px)
      rotateX(${rotateX}deg)
      rotateY(${rotateY}deg)
    `;
  }

  // Drag
  let startX, startY, startPosX, startPosY;

  element.addEventListener('mousedown', (e) => {
    isDragging = true;
    startX = e.clientX;
    startY = e.clientY;
    startPosX = posX;
    startPosY = posY;
    element.style.borderRadius = '50%'; // Morph en cercle
  });

  document.addEventListener('mousemove', (e) => {
    if (isDragging) {
      posX = startPosX + (e.clientX - startX);
      posY = startPosY + (e.clientY - startY);
      // Tilt pendant le drag
      rotateY = (e.clientX - startX) * 0.1;
      rotateX = -(e.clientY - startY) * 0.1;
      updateTransform();
    }
  });

  document.addEventListener('mouseup', () => {
    isDragging = false;
    rotateX = 0;
    rotateY = 0;
    element.style.borderRadius = '20px'; // Retour carre
    updateTransform();
  });
}

Bonnes pratiques

Avant de deployer ces effets en production, gardez ces recommandations en tete :

Performance

  • Utilisez transform plutot que top/left pour les animations (GPU accelere)
  • requestAnimationFrame pour les animations continues
  • will-change avec parcimonie sur les elements qui seront animes
  • Throttle les events mousemove si necessaire

Accessibilite

  • prefers-reduced-motion : Desactivez les animations pour les utilisateurs sensibles
  • Focus visible : Assurez que les elements restent accessibles au clavier
  • ARIA : Ajoutez les attributs appropries pour les lecteurs d'ecran
accessibility.css
@media (prefers-reduced-motion: reduce) {
  .draggable,
  .magnetic-btn,
  .tilt-card,
  .morph-shape {
    transition: none !important;
    animation: none !important;
  }
}

Touch support

N'oubliez pas d'ajouter le support tactile pour les appareils mobiles :

touch-support.js
// Ajoutez les events touch en parallele des events mouse
element.addEventListener('touchstart', handleStart, { passive: false });
element.addEventListener('touchmove', handleMove, { passive: false });
element.addEventListener('touchend', handleEnd);

function handleStart(e) {
  e.preventDefault();
  const touch = e.touches[0];
  // Utiliser touch.clientX et touch.clientY
}

Conclusion

Les elements interactifs transforment une interface ordinaire en une experience memorables. En maitrisant le drag and drop, les effets magnetiques, les animations elastiques et le morphing, vous disposez d'un arsenal complet pour creer des interfaces uniques.

L'important est de doser ces effets avec parcimonie : trop d'interactions peuvent devenir fatigantes. Utilisez-les strategiquement sur vos elements cles (CTAs, cards importantes, navigation) pour maximiser leur impact.

🎨
Allez plus loin

Retrouvez 50+ effets interactifs prets a l'emploi dans notre bibliotheque d'effets, avec code copiable et personnalisable.