Retour au catalogue

Before After Slider

Slider draggable avant/après avec Framer Motion drag='x'. Clip-path animé lié à la position du handle. Auto-animation de présentation au mount (30%→70%→50%). Labels badge flottants.

before-aftermedium Both Responsive a11y
minimalelegantboldagencysaasbeautyreal-estateuniversalcentered
Theme

Creer un slider de comparaison glissable en React

Un slider de comparaison glissable en React decoupe un panneau "avant" via un clip-path CSS mis a jour en temps reel pendant que l'utilisateur fait glisser un handle. L'API drag de Framer Motion gere souris et tactile ; useMotionValue et useTransform convertissent la position du handle en clip-path live sans declencher de re-rendus.

  • Stack : React 18 + Framer Motion 11, aucune librairie d'icones, ~360 lignes au total.
  • API cles : useMotionValue, useTransform, animate, drag + dragConstraints.
  • Animation de presentation automatique au montage (gauche 30%, droite 70%, retour centre) avant la premiere interaction utilisateur.
  • ResizeObserver maintient la largeur du conteneur a jour pour que le pourcentage du clip-path reste precis lors du redimensionnement.
  • Fonctionne sur ecran tactile grace a l'unification des evenements pointeur de Framer Motion ; touchAction: none evite les conflits avec le scroll.

Le Before After Slider est une section React glissable qui permet aux visiteurs de tirer un handle pour reveler une transformation : un redesign, un resultat produit ou une prestation. Le clip-path s'anime en direct sans provoquer de re-rendus React, ce qui maintient un glissement fluide meme sur des appareils moins puissants. Une animation de demonstration se lance au montage pour que les utilisateurs comprennent l'interaction sans aucune instruction.

Anatomie

Le composant comporte trois couches visuelles empilees en absolu dans un conteneur 16:9 : le panneau "apres" en base (pleine largeur, toujours visible), le panneau "avant" par-dessus decoupe a gauche du handle, et un groupe handle glissable (ligne verticale + bouton circulaire + icone double fleche). Deux badges flottants sont epingles dans les coins superieurs et s'affichent en fondu au montage. Un bloc header avec titre et description est place au-dessus du slider, anime a l'entree dans le viewport.

Comment ça marche

Un seul useMotionValue (x) de Framer Motion suit le deplacement horizontal du handle par rapport au centre du conteneur. Le clip-path du panneau avant est derive via useTransform : il convertit x en pourcentage et construit une chaine `inset(0 ${100 - pct}% 0 0)` pour que la zone decoupee retrecisse quand le handle se deplace a droite. La position du label du handle est calculee de la meme facon avec un pourcentage borne. Le prop drag="x" de Framer Motion avec dragConstraints={containerRef} confine le mouvement aux limites du conteneur et gere la capture pointeur, les evenements tactiles et l'annulation de la quantite de mouvement (dragMomentum=false) en un seul prop.

Comment le coder en React

  1. Mettre en place le conteneur et mesurer sa largeur

    Cree une ref div pour le conteneur du slider et utilise un ResizeObserver dans useEffect pour maintenir un state containerWidth synchronise. Cette valeur est necessaire pour convertir le decalage pixel du drag en pourcentage 0-100% pour le clip-path. Mets touchAction: none sur le conteneur pour empecher le navigateur d'intercepter les drags tactiles comme des scrolls.

    const containerRef = useRef<HTMLDivElement>(null);
    const [containerWidth, setContainerWidth] = useState(0);
    useEffect(() => {
      const el = containerRef.current;
      if (!el) return;
      const obs = new ResizeObserver(() => setContainerWidth(el.offsetWidth));
      obs.observe(el);
      setContainerWidth(el.offsetWidth);
      return () => obs.disconnect();
    }, []);
  2. Piloter le clip-path depuis une seule motion value

    Declare un useMotionValue pour le decalage horizontal (x, depart 0 = centre). Derive le clip-path du panneau avant avec useTransform : convertis x en pourcentage borne, puis retourne une chaine inset(). Fais de meme pour la position gauche du handle. Les deux se mettent a jour a 60fps sans toucher au state React.

    const x = useMotionValue(0);
    const clipRight = useTransform(x, (v) => {
      const pct = Math.max(0, Math.min(100, 50 + (v / containerWidth) * 100));
      return `inset(0 ${100 - pct}% 0 0)`;
    });
    const handleLeft = useTransform(x, (v) => {
      const pct = Math.max(2, Math.min(98, 50 + (v / containerWidth) * 100));
      return `${pct}%`;
    });
  3. Attacher le drag au handle et brancher le clip-path sur le panneau avant

    Rends un motion.div handle avec drag="x", dragConstraints={containerRef}, dragElastic={0} et dragMomentum={false}. Passe la motion value x directement comme prop de style x. Applique clipPath={clipRight} au panneau avant en tant que style motion.div. Les deux sont desormais synchronises sans aucun gestionnaire d'evenement.

    <motion.div drag="x" dragConstraints={containerRef}
      dragElastic={0} dragMomentum={false} style={{ x }}>
      {/* handle icon */}
    </motion.div>
    
    <motion.div style={{ clipPath: clipRight, position: "absolute", inset: 0 }}>
      {/* before content */}
    </motion.div>
  4. Ajouter l'animation d'introduction automatique

    Utilise la fonction imperative animate() de Framer Motion pour lancer une sequence (gauche, droite, centre) sur la motion value x apres un court delai, une fois containerWidth disponible. Conditionne cela a un state hasInteracted pour que la sequence s'arrete des que l'utilisateur saisit le handle.

    useEffect(() => {
      if (!containerWidth || hasInteracted) return;
      const half = containerWidth / 2;
      const timer = setTimeout(() => {
        animate(x, -half * 0.4, { duration: 0.9, ease: [0.4, 0, 0.2, 1],
          onComplete: () => animate(x, half * 0.4, { duration: 1.0,
            ease: [0.4, 0, 0.2, 1],
            onComplete: () => animate(x, 0, { duration: 0.7 }) }) });
      }, 600);
      return () => clearTimeout(timer);
    }, [containerWidth, hasInteracted, x]);

Quand l'utiliser

Ce slider excelle partout ou une transformation doit etre ressentie plutot que decrite : outils de retouche photo, avant/apres de redesign, resultats beaute et soin de la peau, revelations de renovation immobiliere, ou tout produit SaaS demontrant un flux avant/apres. Evite-le sur les pages ou le contenu de comparaison n'est pas suffisamment convaincant pour justifier le cout d'interaction, et fournis toujours de vraies images avant et apres en production.

Utilisé par

  • Shopify, Utilise des comparaisons avant/apres dans les temoignages marchands pour montrer visuellement les transformations de boutique.
  • Canva, Deploie des sliders de comparaison dans les pages de fonctionnalites pour mettre en avant l'effacement d'arriere-plan par IA et les resultats de retouche photo.
  • Figma, Utilise des comparaisons de type revelation dans les annonces de versions pour presenter cote a cote les ameliorations d'interface.
  • Lightroom (Adobe), La bascule avant/apres est un pattern d'edition central dans ses applications web et mobile pour comparer photos brutes et retouchees.

FAQ

Pourquoi utiliser useMotionValue plutot que le state React pour la position du drag ?

useMotionValue se met a jour en dehors du cycle de rendu React, donc chaque pixel de drag ne declenche pas de re-rendu. Le clip-path et la position du handle se mettent a jour au taux de rafraichissement de l'animation (jusqu'a 120fps) sans le cout d'un setState, c'est pourquoi le slider reste fluide meme sur du materiel moins puissant.

Comment l'approche clip-path se compare-t-elle a deux div en position absolue avec overflow:hidden ?

L'approche clip-path est composited sur GPU et n'entraine pas de recalculs de layout, contrairement au redimensionnement de la largeur d'une div a chaque frame. Elle gere aussi plus proprement la ligne de separation et les formes non rectangulaires. La contrepartie est que les animations clip-path peuvent provoquer de l'aliasing sous-pixel sur certains ecrans au niveau du bord.

Le drag fonctionne-t-il sur ecran tactile ?

Oui. L'API drag de Framer Motion unifie les evenements souris et pointeur, donc le drag tactile fonctionne sans configuration supplementaire. Le style touchAction: none sur le conteneur est obligatoire pour empecher le navigateur de s'approprier l'evenement tactile pour le scroll de page avant que Framer Motion ne puisse le traiter.

Comment passer de vraies images a la place des maquettes de substitution ?

Passe les props beforeImage et afterImage sous forme d'URL absolues ou de chemins relatifs. Quand elles sont definies, le composant les affiche comme background-image CSS en center/cover, remplacant completement les maquettes de substitution. Les deux props sont optionnelles donc le composant fonctionne comme demo visuelle sans configuration.

"use client";

import { useRef, useEffect, useState } from "react";
import { motion, useMotionValue, useTransform, animate } from "framer-motion";

interface BeforeAfterSliderProps {
  title?: string;
  description?: string;
  beforeLabel?: string;
  afterLabel?: string;
  beforeImage?: string;
  afterImage?: string;
  beforeColor?: string;
  afterColor?: string;
}

const EASE = [0.16, 1, 0.3, 1] as const;

export default function BeforeAfterSlider({
  title = "Avant / Après",
  description = "Faites glisser le curseur pour révéler la transformation",
  beforeLabel = "Avant",

Code complet réservé à Pro

Code source intégral, export multi-framework et playground.

Passer en Pro, 9,99€/mois

Avis

Slider de comparaison React glisser-pour-reveler, Tutoriel