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

Cómo crear una sección de contacto con globo en React con SVG y Framer Motion

Una sección de contacto con globo SVG en React dibuja elipses de latitud y de longitud como trazos discontinuos, y luego coloca los marcadores de oficinas como círculos concéntricos a partir de coordenadas lat/lng proyectadas en el espacio SVG. whileInView de Framer Motion anima la entrada de cada elemento al hacer scroll, y un div conic-gradient gira de forma continua durante 60s para aportar profundidad sin física en JavaScript.

  • Stack: React + Framer Motion 11 + lucide-react, ~155 líneas en total, cero canvas ni librería de globo de terceros.
  • Globo dibujado en SVG puro: 5 elipses de latitud, 6 de longitud, círculos de oficinas animados con keyframes de scale.
  • La animación pathLength traza cada línea de la cuadrícula de 0 a 1 al entrar con el scroll, escalonada mediante delay.
  • Accesible: todo el texto está en el DOM (elementos SVG <text> y el panel derecho), no incrustado en una imagen.
  • En pantallas estrechas la cuadrícula de dos columnas se apila verticalmente; el globo se mantiene cuadrado de 320px y centrado.

Contact Globe es una sección de React en split-layout que combina un globo SVG animado a la izquierda con un título, una descripción e información de contacto con iconos a la derecha. Transmite presencia internacional sin importar una librería de mapas ni un motor de canvas: todo el globo se reduce a unas pocas elipses SVG, un brillo conic-gradient y algunos disparadores de scroll de Framer Motion.

Anatomía

La sección es una cuadrícula CSS de dos columnas (1fr 1fr, gap de 4rem). La columna izquierda contiene el subcomponente Globe: un SVG de 320px que dibuja el círculo exterior, elipses de latitud discontinuas, elipses de longitud discontinuas y grupos de marcadores por oficina (un gran círculo de pulso tenue, un punto sólido y la etiqueta de la ciudad). Un div conic-gradient en rotación se sitúa en absoluto sobre el SVG con pointer-events:none para el efecto de halo. La columna derecha apila la etiqueta eyebrow, un h2 serif en cursiva, un párrafo y una lista vertical de filas de contacto, cada una compuesta por una caja de icono y dos líneas de texto.

Cómo funciona

Cada elemento animado usa whileInView para dispararse una sola vez cuando la sección entra en el viewport con el scroll. Las líneas de la cuadrícula usan la propiedad pathLength de Framer Motion: cada elipse comienza en pathLength:0 y anima hasta pathLength:1, de modo que las líneas se dibujan solas al entrar. Los puntos de las oficinas usan una secuencia de keyframes de scale [0, 1.6, 1] para producir un pequeño rebote elástico. Las filas de contacto entran deslizándose desde x:20 con un delay incremental (0.2 + i * 0.08s). La rotación del conic-gradient es un `animate={{ rotate: 360 }}` aparte con `repeat: Infinity` y ease:"linear" a 60s, lo que aporta un brillo ambiental lento sin afectar al rendimiento del scroll.

Cómo crearlo en React

  1. Dibujar la cuadrícula del globo en SVG

    Crea un SVG de 320px. Dibuja el círculo de borde exterior y luego recorre un array de latitudes (-60, -30, 0, 30, 60) para producir elipses cuyo radio vertical es (lat/90)*r y cuyo radio horizontal usa una proyección coseno. Haz lo mismo para las longitudes con una proyección seno. Usa strokeDasharray="3 6" para la textura de la cuadrícula.

    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. Animar las líneas de la cuadrícula al hacer scroll con pathLength

    Envuelve cada elipse en un motion.ellipse. Pon initial={{ pathLength: 0 }} y whileInView={{ pathLength: 1 }} con viewport={{ once: true }}. Escalona las líneas de latitud en delay:0.2 y las de longitud en delay:0.4 para que la cuadrícula se dibuje capa por capa.

    <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. Colocar los marcadores de oficinas desde las coordenadas lat/lng

    Proyecta cada oficina en x/y SVG mediante una proyección lineal escalada por 0.7 para mantener los marcadores dentro de la esfera. Renderiza dos círculos por marcador: uno grande y tenue con keyframes de scale [0, 1.6, 1] para el pulso elástico, y uno pequeño y sólido. Añade un motion.text encima para la etiqueta de la ciudad.

    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. Añadir el brillo ambiental conic-gradient

    Posiciona un div en absoluto sobre el SVG con borderRadius:"50%" y un conic-gradient que va de transparente al color de acento y vuelve. Pon opacity en 0.06 y pointer-events:none, luego anima rotate de 0 a 360 con repeat:Infinity y ease:"linear" a 60s. Añade un barrido de luz sutil sin 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",
      }}
    />

Cuándo usarlo

Recurre a esta sección cuando un producto o una agencia tiene varias oficinas y quiere comunicar su alcance internacional sin un embed de mapa pesado. Funciona bien como bloque de contacto principal en una página Sobre nosotros o Contacto, acompañada de un formulario o un CTA debajo. Evítala cuando la empresa tiene una sola sede o cuando el peso de la página es una preocupación: aunque el SVG es ligero, el presupuesto de animación no es trivial en dispositivos de gama baja. En pantallas táctiles el layout se apila correctamente, pero el globo es puramente visual y sin interactividad.

Usado por

  • Stripe, Usa visualizaciones estilo globo en sus páginas de empleo e infraestructura para ilustrar la cobertura mundial de su red de pagos.
  • Cloudflare, Muestra un globo de red animado como visual hero en su página de inicio y en sus páginas de producto CDN para transmitir presencia edge en todos los continentes.
  • Vercel, Presenta un globo de red edge en sus páginas de marketing de infraestructura, mostrando las ubicaciones de despliegue como puntos luminosos sobre una esfera oscura.
  • Miro, Usa visuales de mapamundi y globo en sus páginas Enterprise y Sobre nosotros para reforzar el mensaje de un equipo y una base de clientes distribuidos por todo el mundo.

FAQ

¿Usa una proyección cartográfica real?

No. Usa un mapeo lineal simple: la longitud corresponde a x y la latitud a y, ambas escaladas por 0.7r para mantenerse dentro de la esfera. Una precisión geográfica real necesitaría una proyección equirectangular o Mercator adecuada, pero para una sección de contacto la aproximación es indistinguible a simple vista.

¿Puedo reemplazar el globo SVG por un globo canvas o Three.js?

Sí, el subcomponente Globe es autónomo y solo recibe un array de offices. Cámbialo por el motor de renderizado que prefieras. El panel derecho es independiente y no cambiará.

¿Por qué cada línea de la cuadrícula se anima por separado en lugar de que todo el globo aparezca con un fundido?

El trazado pathLength escalonado revela la estructura del globo de forma progresiva, lo que se percibe como si el globo se construyera en tiempo real. Un único fundido pierde esa cualidad narrativa. Los delays son intencionadamente cortos (0.2-0.4s) para que no se sienta lento.

¿Cómo hago el layout responsive?

La cuadrícula actual está definida con gridTemplateColumns:"1fr 1fr" en estilo inline. Para apilar en móvil, cambia a una media query o a una clase de Tailwind como `grid-cols-1 md:grid-cols-2`. El componente Globe ya está centrado con margin:"0 auto", así que se coloca correctamente en una sola columna.

"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

Reseñas

Sección de contacto con globo SVG en React, código +