Retour au catalogue

Contact Globe

Section contact avec globe SVG stylise (lat/lng, rotation lente). Points lumineux pour les bureaux. Infos a cote.

contactcomplex Both Responsive a11y
bolddarkcorporatesaasagencyuniversalsplit
Theme

Créer une section contact avec globe SVG React et Framer Motion

Une section contact globe SVG React dessine des ellipses de latitude et de longitude sous forme de tirets, puis place des épingles de bureaux via des cercles concentriques calculés depuis des coordonnées lat/lng projetées en espace SVG. Framer Motion whileInView anime l'entrée de chaque élément au scroll, et un div conic-gradient tourne en continu sur 60s pour donner de la profondeur sans physique JavaScript.

  • Stack : React + Framer Motion 11 + lucide-react, ~155 lignes au total, zéro canvas ni bibliothèque de globe tierce.
  • Globe dessiné en SVG pur : 5 ellipses de latitude, 6 de longitude, cercles de bureaux animés avec des keyframes scale.
  • L'animation pathLength trace chaque ligne de grille de 0 à 1 à l'entrée au scroll, décalée par delay.
  • Accessible : tout le texte est dans le DOM (éléments SVG <text> et panneau droit), pas intégré dans une image.
  • Sur écrans étroits la grille deux colonnes s'empile verticalement ; le globe reste 320px carré, centré.

Contact Globe est une section React en split-layout qui associe un globe SVG animé à gauche à un titre, une description et des infos de contact avec icônes à droite. Il signale une présence internationale sans importer de bibliothèque de carte ni de moteur canvas, l'ensemble du globe tient en quelques ellipses SVG, un reflet conic-gradient et des déclencheurs de scroll Framer Motion.

Anatomie

La section est une grille CSS deux colonnes (1fr 1fr, gap 4rem). La colonne gauche contient le sous-composant Globe : un SVG de 320px qui dessine le cercle extérieur, des ellipses de latitude en tirets, des ellipses de longitude en tirets et des groupes de marqueurs par bureau (un grand cercle pulsé transparent, un point solide et le nom de la ville). Un div conic-gradient en rotation est positionné en absolu sur le SVG avec pointer-events:none pour l'effet de halo. La colonne droite empile le label eyebrow, un h2 serif italique, un paragraphe et une liste verticale de lignes de contact, chacune composée d'une boîte d'icône et de deux lignes de texte.

Comment ça marche

Chaque élément animé utilise whileInView pour se déclencher une seule fois à l'entrée dans le viewport au scroll. Les lignes de grille utilisent la propriété pathLength de Framer Motion, chaque ellipse commence à pathLength:0 et anime vers pathLength:1, dessinant les lignes à l'entrée. Les points de bureaux utilisent une séquence de keyframes scale [0, 1.6, 1] pour un petit rebond élastique. Les lignes de contact glissent depuis x:20 avec un delay incrémental (0.2 + i * 0.08s). La rotation du conic-gradient est un `animate={{ rotate: 360 }}` séparé avec `repeat: Infinity` et ease:"linear" à 60s, donnant un reflet ambiant lent sans impacter les performances au scroll.

Comment le coder en React

  1. Dessiner la grille du globe en SVG

    Crée un SVG de 320px. Dessine le cercle de bordure extérieur, puis parcours un tableau de latitudes (-60, -30, 0, 30, 60) pour produire des ellipses dont le rayon vertical est (lat/90)*r et le horizontal utilise la projection cosinus. Fais de même pour les longitudes avec la projection sinus. Utilise strokeDasharray="3 6" pour la texture de grille.

    const latLines = [-60, -30, 0, 30, 60];
    latLines.map((lat) => {
      const y = cy - (lat / 90) * r;
      const rx = Math.cos((lat * Math.PI) / 180) * r;
      return <ellipse cx={cx} cy={y} rx={rx} ry={rx * 0.15} strokeDasharray="3 6" />;
    })
  2. Animer les lignes de grille au scroll avec pathLength

    Enveloppe chaque ellipse dans un motion.ellipse. Pose initial={{ pathLength: 0 }} et whileInView={{ pathLength: 1 }} avec viewport={{ once: true }}. Décale les lignes de latitude à delay:0.2 et les lignes de longitude à delay:0.4 pour que la grille se dessine couche par couche.

    <motion.ellipse
      initial={{ pathLength: 0 }}
      whileInView={{ pathLength: 1 }}
      viewport={{ once: true }}
      transition={{ duration: 1.2, delay: 0.2, ease: [0.16, 1, 0.3, 1] }}
    />
  3. Placer les épingles de bureaux depuis les coordonnées lat/lng

    Projette chaque bureau en x/y SVG via une projection linéaire mise à l'échelle par 0.7 pour garder les épingles à l'intérieur de la sphère. Rends deux cercles par épingle : un grand transparent avec des keyframes scale [0, 1.6, 1] pour le pulse élastique, et un petit solide. Ajoute un motion.text au-dessus pour le nom de la ville.

    const x = cx + (office.lng / 180) * r * 0.7;
    const y = cy - (office.lat / 90) * r * 0.7;
    // pulse circle
    <motion.circle r={12} opacity={0.15}
      whileInView={{ scale: [0, 1.6, 1] }}
      transition={{ duration: 0.8, delay: 0.6 + i * 0.15 }} />
    // solid dot
    <motion.circle r={4}
      whileInView={{ scale: 1 }} initial={{ scale: 0 }} />
  4. Ajouter le reflet ambiant conic-gradient

    Positionne un div en absolu sur le SVG avec borderRadius:"50%" et un conic-gradient qui va du transparent à la couleur accent et revient. Mets opacity à 0.06 et pointer-events:none, puis anime rotate de 0 à 360 avec repeat:Infinity et ease:"linear" à 60s. Il ajoute un balayage lumineux subtil sans canvas ni WebGL.

    <motion.div
      animate={{ rotate: 360 }}
      transition={{ duration: 60, repeat: Infinity, ease: "linear" }}
      style={{
        position: "absolute", inset: 0, borderRadius: "50%",
        background: "conic-gradient(from 0deg, transparent 0%, var(--color-accent) 5%, transparent 10%)",
        opacity: 0.06, pointerEvents: "none",
      }}
    />

Quand l'utiliser

Utilise cette section quand un produit ou une agence a plusieurs bureaux et veut communiquer sa portée internationale sans embed de carte lourde. Elle fonctionne bien comme bloc de contact principal sur une page À propos ou Contact, associée à un formulaire ou un CTA en dessous. À éviter si l'entreprise est mono-site ou si le poids de page est une contrainte, même si le SVG est léger, le budget animation est non-négligeable sur les appareils bas de gamme. Sur écrans tactiles le layout s'empile correctement mais le globe est purement visuel sans interactivité.

Utilisé par

  • Stripe, Utilise des visualisations style globe sur ses pages carrières et infrastructure pour illustrer la couverture mondiale de son réseau de paiement.
  • Cloudflare, Affiche un globe réseau animé en visuel hero sur sa page d'accueil et ses pages produit CDN pour exprimer une présence edge sur tous les continents.
  • Vercel, Présente un globe de réseau edge sur ses pages marketing infrastructure, affichant les emplacements de déploiement comme des points lumineux sur une sphère sombre.
  • Miro, Utilise des visuels de carte du monde et de globe sur ses pages Enterprise et À propos pour renforcer le message d'une équipe et d'une base clients mondialement réparties.

FAQ

Est-ce que ça utilise une vraie projection cartographique ?

Non. Il utilise un simple mappage linéaire : la longitude correspond à x et la latitude à y, toutes deux mises à l'échelle par 0.7r pour rester dans la sphère. Une vraie précision géographique nécessiterait une projection équirectangulaire ou Mercator correcte, mais pour une section contact l'approximation est indiscernable à l'oeil.

Puis-je remplacer le globe SVG par un globe canvas ou Three.js ?

Oui, le sous-composant Globe est autonome et ne reçoit qu'un tableau offices. Remplace-le par n'importe quel moteur de rendu. Le panneau droit est indépendant et ne changera pas.

Pourquoi chaque ligne de grille s'anime-t-elle séparément au lieu que le globe entier apparaisse en fondu ?

Le tracé pathLength décalé révèle la structure du globe progressivement, ce qui donne l'impression que le globe se construit en temps réel. Un fondu unique perd cette qualité narrative. Les délais sont intentionnellement courts (0.2-0.4s) pour ne pas paraître lent.

Comment rendre le layout responsive ?

La grille actuelle est définie avec gridTemplateColumns:"1fr 1fr" en style inline. Pour empiler sur mobile, passe à une media query ou une classe Tailwind comme `grid-cols-1 md:grid-cols-2`. Le composant Globe est déjà centré avec margin:"0 auto" donc il se place correctement dans une seule colonne.

"use client";

import { motion } from "framer-motion";
import { Mail, MapPin, Phone } from "lucide-react";

interface Office {
  city: string;
  lat: number;
  lng: number;
}

interface ContactInfo {
  icon: "mail" | "phone" | "location";
  label: string;
  value: string;
}

interface ContactGlobeProps {
  heading?: string;
  subtitle?: string;
  description?: string;
  offices?: Office[];

Code complet réservé à Pro

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

Passer en Pro, 9,99€/mois

Avis

Section contact globe SVG React, Code + Tutoriel