@xingwangzhe/tags-cloud: El Motor de Nube de Etiquetas 3D Multi-Modal Construido sobre Matemáticas Puras
@xingwangzhe/tags-cloud: El Motor de Nube de Etiquetas 3D Multi-Modal Construido sobre Matemáticas Puras
GitHub: xingwangzhe/tags-cloud · Demo: tagscloud.needhelp.icu · npm: @xingwangzhe/tags-cloud · Licencia: MIT · Versión: v0.9.0
1. Introducción: Reimaginando la Nube de Etiquetas 3D para la Web Moderna
La nube de etiquetas ha sido un elemento básico de la visualización web durante más de dos décadas, evolucionando desde simples listas de palabras ponderadas hasta experiencias tridimensionales inmersivas que cautivan a los usuarios y transforman colecciones de texto mundanas en esculturas espaciales interactivas. Entre las contribuciones más influyentes a esta evolución se encuentra cong-min/TagCloud, una librería JavaScript ligera que demostró cómo se podía lograr una esfera de etiquetas de texto rotativas con un costo mínimo y cero dependencias externas. Con 390 estrellas y 93 forks en GitHub, el TagCloud original estableció un patrón de diseño que innumerables desarrolladores han adoptado para sitios de portafolio, vitrinas de habilidades y paneles de visualización de datos. Su innovación central consistió en demostrar que las nubes de etiquetas 3D no necesitan depender de frameworks WebGL pesados ni motores de renderizado externos; en cambio, cálculos trigonométricos directos combinados con un cuidadoso posicionamiento DOM podían producir resultados visuales sorprendentemente convincentes.
Presentamos @xingwangzhe/tags-cloud — una reinvención desde cero del concepto de nube de etiquetas 3D que preserva la elegancia matemática de su predecesor mientras expande drásticamente sus capacidades, moderniza su arquitectura y empuja los límites de lo que una nube de etiquetas puede renderizar. Esto no es meramente un fork o una adición de funcionalidades; representa un cambio arquitectónico fundamental de un renderizador de solo texto y con mucho DOM a un motor multi-modal capaz de mostrar texto, imágenes, gráficos SVG, HTML arbitrario, elementos de video e incluso Web Components completos — todos rotando juntos en un espacio tridimensional matemáticamente preciso. La librería logra esta versatilidad mientras mantiene una huella extraordinariamente compacta de aproximadamente 3KB comprimido con gzip, sin dependencias en tiempo de ejecución, y construida con una toolchain completamente moderna que comprende Bun como runtime JavaScript, Vite como bundler y TypeScript para seguridad de tipos completa en toda la base de código.
El eslogan del proyecto — “nube de etiquetas 3D multi-modal · motor de matemáticas puras” — encapsula su doble identidad. Por un lado, es una librería de componentes UI práctica que los desarrolladores pueden integrar en cualquier proyecto web para crear esferas de etiquetas visualmente impactantes. Por otro lado, es un motor matemático que descompone el complejo problema del renderizado de nubes de etiquetas 3D en tres operaciones geométricas puras, deterministas y hermosamente separables: distribución de esfera de Fibonacci para el posicionamiento inicial, multiplicación de matrices de rotación para la transformación espacial y proyección perspectiva para el renderizado 2D consciente de profundidad. Cada una de estas operaciones está implementada en su propio módulo dedicado dentro del directorio src/core/, haciendo que la base de código no solo sea altamente mantenible sino también un excelente recurso educativo para cualquiera que busque entender las matemáticas detrás de los gráficos 3D por computadora sin la complejidad oscurecedora de un pipeline de renderizado completo.
Las siguientes secciones proporcionan una inmersión técnica exhaustiva en cada aspecto de @xingwangzhe/tags-cloud, desde el algoritmo de distribución de esfera derivado de la proporción áurea hasta el modelo de interacción arcball inspirado en cuaterniones, desde la arquitectura de renderizado dual Canvas/DOM hasta las consideraciones de diseño responsivo que lo hacen funcionar sin problemas en dispositivos de escritorio y móviles. Ya seas un desarrollador evaluando esta librería para tu próximo proyecto, un programador de gráficos interesado en técnicas 3D ligeras, o un matemático curioso sobre las aplicaciones del mundo real de la red de Fibonacci, este artículo ofrece explicaciones detalladas, derivaciones matemáticas, diagramas arquitectónicos y ejemplos de código anotados para iluminar cada faceta de esta notable pieza de ingeniería de software.
2. El Fundamento Matemático: Tres Pilares del Renderizado 3D
En el corazón de @xingwangzhe/tags-cloud yace un trío de operaciones matemáticas que, al componerse, transforman una lista abstracta de elementos de contenido en una esfera rotatoria visualmente coherente. Estas operaciones — distribución, rotación y proyección — están implementadas en tres módulos dedicados bajo src/core/, cada uno responsable de una única transformación geométrica. Esta separación de responsabilidades no es meramente una preferencia arquitectónica; refleja la estructura fundamental de los pipelines de gráficos 3D por computadora, donde la generación de vértices, la transformación del modelo y la proyección de cámara son etapas distintas que pueden optimizarse, probarse y entenderse de forma independiente.
flowchart LR
subgraph Input["📥 Capa de Entrada"]
TAGS["TagItem[]
(text | image | SVG | HTML | video | element)"]
OPTS["TagCloudOptions
(radius, spin, colors, callbacks)"]
end
subgraph CoreEngine["⚙️ Motor de Matemáticas Puras (src/core/)"]
direction TB
DIST["🔵 distribution.ts
Esfera de Fibonacci
N puntos → coordenadas 3D"]
ROT["🟢 rotation.ts
Matrices de Rotación
puntos 3D → puntos 3D rotados"]
PROJ["🔴 projection.ts
Proyección Perspectiva
3D rotado → 2D + profundidad"]
end
subgraph RenderLayer["🎨 Capa de Renderizado"]
direction TB
CANVAS["Canvas 2D API
(texto + imágenes)"]
DOM["Superposición DOM
(SVG + HTML + video + elementos)"]
end
subgraph Interaction["👤 Capa de Interacción"]
MOUSE["Arrastre de Mouse / Táctil"]
INERTIA["Decaimiento de Inercia
(0.96/frame)"]
end
TAGS --> DIST
OPTS --> DIST
DIST -->|"Vec3[]"| ROT
ROT -->|"Vec3[] rotados"| PROJ
PROJ -->|"ProjectedTag[]
(x, y, z, scale, alpha)"| CANVAS
PROJ --> DOM
MOUSE --> INERTIA
INERTIA -->|"ángulos de rotación"| ROT
style CoreEngine fill:#1a1a2e,stroke:#16213e
style Input fill:#0f3460,stroke:#16213e
style RenderLayer fill:#533483,stroke:#16213e
style Interaction fill:#e94560,stroke:#16213e,color:#fff
2.1 Distribución de Esfera de Fibonacci: La Geometría Áurea del Posicionamiento de Puntos
El primer paso, y probablemente el más crítico desde el punto de vista estético, para crear una nube de etiquetas 3D es determinar dónde debe colocarse cada etiqueta en la esfera. Un enfoque ingenuo podría colocar etiquetas en intervalos regulares de latitud y longitud, pero esto inevitablemente lleva a agrupaciones en los polos, donde convergen las líneas de meridiano, y regiones dispersas cerca del ecuador. El algoritmo de esfera de Fibonacci resuelve este problema elegantemente produciendo una distribución de puntos notablemente uniforme en toda la superficie de la esfera, sin agrupaciones ni espacios visibles independientemente del número de puntos.
Este algoritmo toma su nombre de la proporción áurea (\varphi = \frac{1 + \sqrt{5}}{2} \approx 1.6180339887), una constante matemática que aparece en toda la naturaleza en estructuras que van desde la disposición de semillas de girasol hasta las espirales de conchas de nautilus. La conexión con los números de Fibonacci — la secuencia donde cada término es la suma de los dos anteriores — es que la proporción de números de Fibonacci consecutivos converge a la proporción áurea a medida que la secuencia progresa. En el contexto de la distribución en una esfera, la irracionalidad de la proporción áurea asegura que los puntos sucesivos siempre se colocan en ángulos que nunca se repiten, creando un patrón espiral aperiódico que llena la superficie de la esfera de manera uniforme.
La implementación específica en src/core/distribution.ts usa una variación de la red de Fibonacci que ha sido optimizada para mapeo esférico. Para una esfera de radio (R) con (N) puntos a distribuir, el algoritmo calcula las coordenadas esféricas y luego las convierte a coordenadas cartesianas ((x, y, z)) para cada punto (i \in {0, 1, 2, \ldots, N-1}). El ángulo de latitud (\phi) (medido desde el eje z positivo, es decir, el polo Norte) se calcula usando la función arcocoseno para asegurar una distribución uniforme a lo largo del eje z:
Esta fórmula coloca el primer punto ligeramente debajo del polo Norte y el último punto ligeramente arriba del polo Sur, con el desplazamiento (+0.5) (incorporado en el numerador (2i + 1)) asegurando que ningún punto caiga exactamente sobre un polo. Este ajuste sutil es crucial porque previene el artefacto visual de una etiqueta situada directamente en la parte superior o inferior de la esfera donde la rotación sería apenas perceptible. La función (\arccos) transforma un parámetro lineal distribuido uniformemente en una distribución angular ponderada por coseno, lo que compensa el hecho de que las esferas tienen menos área superficial cerca de los polos y más cerca del ecuador.
El ángulo de longitud (\theta) (medido en el plano xy desde el eje x positivo) se calcula usando un escalado de (\phi) por (\sqrt{N\pi}), lo que crea el patrón espiral característico:
El factor (\sqrt{N\pi}) se deriva de la relación entre el ángulo áureo y el área superficial total de la esfera. A medida que (N) aumenta, este escalado asegura que la espiral se enrolle alrededor de la esfera el número apropiado de veces para mantener un espaciado uniforme entre puntos vecinos. El resultado es un patrón donde cada punto sucesivo está rotado aproximadamente por el ángulo áureo (\approx 137.5°) en relación con su predecesor cuando se ve a lo largo del eje z, reflejando las espirales filotácticas encontradas en la naturaleza.
Finalmente, estas coordenadas esféricas se convierten a coordenadas cartesianas usando la transformación estándar:
Los puntos resultantes satisfacen la ecuación de la esfera (x_i^2 + y_i^2 + z_i^2 = R^2) y se distribuyen con una uniformidad que rivaliza con métodos basados en optimización mucho más costosos computacionalmente. El siguiente recuadro resume el algoritmo completo de la esfera de Fibonacci:
La implementación en TypeScript en src/core/distribution.ts es notablemente concisa, reflejando la elegante simplicidad del algoritmo:
export interface Vec3 { x: number; y: number; z: number; }
export function fibonacciSphere(n: number, R: number): Vec3[] { const points: Vec3[] = []; for (let i = 0; i < n; i++) { const phi = Math.acos(-1 + (2 * i + 1) / n); const theta = Math.sqrt(n * Math.PI) * phi; points.push({ x: R * Math.cos(theta) * Math.sin(phi), y: R * Math.sin(theta) * Math.sin(phi), z: R * Math.cos(phi), }); } return points;}Esta función devuelve un array de objetos Vec3 que sirven como las posiciones iniciales para todas las etiquetas en la nube. El algoritmo se ejecuta en tiempo (\mathcal{O}(N)), lo que lo hace adecuado para nubes de etiquetas con cientos de elementos. En la práctica, la mayoría de las nubes de etiquetas contienen entre 20 y 100 etiquetas, para las cuales este cálculo es virtualmente instantáneo.
2.2 Rotación 3D: Multiplicación de Matrices en el Dominio Espacial
Una vez que las etiquetas están posicionadas en la superficie de la esfera, el siguiente paso es permitir la rotación — tanto el giro automático continuo como la interacción de arrastre por parte del usuario. La librería implementa la rotación usando matrices de rotación compuestas alineadas a los ejes, específicamente rotaciones secuenciales alrededor del eje Y seguidas del eje X. Este enfoque es computacionalmente eficiente, fácil de entender y completamente suficiente para el caso de uso de nubes de etiquetas donde las rotaciones siempre son relativas a una esfera centrada en el origen.
El fundamento matemático para estas rotaciones proviene del álgebra lineal. Una matriz de rotación 3D es una matriz ortogonal (3 \times 3) con determinante 1 que transforma las coordenadas de un punto mientras preserva su distancia al origen. Para la rotación por un ángulo (\alpha) alrededor del eje Y, la matriz de rotación (R_y(\alpha)) es:
Para la rotación por un ángulo (\beta) alrededor del eje X, la matriz de rotación (R_x(\beta)) es:
La librería aplica estas rotaciones secuencialmente: primero la rotación alrededor del eje Y, luego la rotación alrededor del eje X. Para un punto (P = (x, y, z)), la rotación alrededor del eje Y produce un punto intermedio (P’):
Luego se aplica la rotación alrededor del eje X a (P’) para producir el punto rotado final (P’’):
La implementación en src/core/rotation.ts expande estas multiplicaciones de matrices en operaciones aritméticas directas, evitando la sobrecarga de la asignación y multiplicación de objetos matriz. Para un solo punto, la función de rotación calcula:
La función rotatePoints realiza esta transformación en lote para todas las etiquetas:
export function rotatePoints(points: Vec3[], a: number, b: number): Vec3[] { const sinA = Math.sin(a), cosA = Math.cos(a); const sinB = Math.sin(b), cosB = Math.cos(b); return points.map((p) => { const y1 = p.y * cosA + p.z * -sinA; const z1 = p.y * sinA + p.z * cosA; const x2 = p.x * cosB + z1 * sinB; return { x: x2, y: y1, z: z1 * cosB - p.x * sinB }; });}2.3 Proyección Perspectiva: Del Espacio 3D a la Pantalla 2D
La operación matemática final transforma las coordenadas 3D rotadas en posiciones de pantalla 2D. La librería usa un modelo de proyección simplificado pero altamente efectivo que calcula un factor de escala y un factor de opacidad basados en la profundidad, con un parámetro de profundidad de perspectiva igual al doble del radio de la esfera:
El factor de opacidad (alpha) usa una relación cuadrática con un ajuste de sesgo:
El pipeline de proyección completo se resume como:
La implementación en TypeScript refleja estas matemáticas con precisión:
export function project( points: { x: number; y: number; z: number }[], depth: number,): ProjectedTag[] { const d2 = 2 * depth; return points.map((p) => { const per = d2 / (d2 + p.z); const alpha = Math.min(1, Math.max(0, per * per - 0.25)); return { x: p.x, y: p.y, z: p.z, scale: per, alpha }; });}3. Arquitectura y Diseño de Módulos
La base de código ejemplifica una arquitectura de software limpia mediante la separación deliberada del cálculo matemático, la orquestación del renderizado y el manejo de la interacción del usuario.
3.1 El Motor de Matemáticas Puras: src/core/
| Módulo | Función | Entrada | Salida | Fórmula Clave |
|---|---|---|---|---|
| distribution.ts | fibonacciSphere(n, R) | N, R | Vec3[] | (\phi_i = \arccos(-1 + \frac{2i+1}{N})) |
| rotation.ts | rotatePoints(points, a, b) | Vec3[], a, b | Vec3[] | (R_y(a)) · (R_x(b)) · (P) |
| projection.ts | project(points, depth) | Vec3[], d | ProjectedTag[] | per = 2d/(2d+z), (\alpha) = per(^2) - 0.25 |
3.2 El Sistema de Renderizado Dual: Canvas + DOM
Una de las innovaciones más significativas es su sistema de renderizado dual, que usa la API Canvas 2D para texto e imágenes, y una superposición DOM para SVG, HTML, video y Web Components.
flowchart TB
subgraph "Tipos de Etiqueta y Renderizadores"
direction LR
TEXT["📄 Etiqueta de Texto"]
IMG["🖼️ Etiqueta de Imagen"]
SVG_TAG["🎨 Etiqueta SVG"]
HTML_TAG["🌐 Etiqueta HTML"]
VIDEO["🎬 Etiqueta de Video"]
ELEMENT["🔧 Etiqueta de Elemento"]
end
subgraph "Selección de Renderizador"
CANVAS["Canvas 2D API
✅ Texto
✅ Imágenes"]
DOM["Superposición DOM
✅ SVG
✅ HTML
✅ Video
✅ Web Components"]
end
TEXT --> CANVAS
IMG --> CANVAS
SVG_TAG --> DOM
HTML_TAG --> DOM
VIDEO --> DOM
ELEMENT --> DOM
style CANVAS fill:#0f3460,stroke:#e94560,color:#fff
style DOM fill:#533483,stroke:#e94560,color:#fff
4. Características de Rendimiento
4.1 Complejidad Computacional
| Etapa | Complejidad | Operaciones Dominantes |
|---|---|---|
| Rotación | O(N) | 8 mul + 4 sum por punto |
| Proyección | O(N) | 1 div + 2 mul + 1 sum por punto |
| Z-sort | O(N log N) | Ordenamiento por comparación |
| Renderizado Canvas | O(N) | Llamadas fillText / drawImage |
| Actualización DOM | O(N) | transform + opacity set |
4.2 Eficiencia de Memoria
Para (N = 100) etiquetas, el estado numérico total es de aproximadamente (100 \times (3 + 5) \times 8 = 6,400) bytes — menos de 7KB de números de punto flotante.
4.3 Tamaño del Bundle
| Formato | Tamaño |
|---|---|
| ESM (dist/index.js) | ~12 KB |
| Gzip | ~3 KB |
| Brotli | ~2.5 KB |
5. Contenido Multi-Modal: Seis Tipos de Etiqueta
| Tipo de Etiqueta | Formato de Entrada | Renderizador | Interactividad | Caso de Uso |
|---|---|---|---|---|
| Texto | string | Canvas 2D | callback onTagClick | Habilidades, palabras clave |
| Imagen | { type:“image”, src, w, h } | Canvas 2D | onClick por etiqueta | Logos, avatares |
| SVG | { type:“svg”, content, w, h } | DOM | onClick por etiqueta | Iconos vectoriales |
| HTML | { type:“html”, html } | DOM | onClick por etiqueta | Texto con formato enriquecido |
| Video | { type:“video”, src, w, h } | DOM | Pantalla completa al hacer clic | Clips de demostración |
| Elemento | { type:“element”, element } | DOM | Eventos DOM nativos | Web Components |
6. Comparación con cong-min/TagCloud
| Aspecto | cong-min/TagCloud | @xingwangzhe/tags-cloud | Mejora |
|---|---|---|---|
| Lenguaje | JavaScript (ES5) | TypeScript | Seguridad de tipos completa |
| Tamaño del Bundle | ~6KB minificado | ~3KB comprimido con gzip | ~50% más pequeño |
| Dependencias | 0 | 0 | Ambos sin dependencias |
| Tipos de Etiqueta | Solo texto | Texto, Imagen, SVG, HTML, Video, Elemento | 6x modalidades de contenido |
| Renderizador | Solo DOM | Canvas 2D + DOM híbrido | Mejor rendimiento |
| Herramienta de Build | Rollup + Babel | Vite + Bun + Oxlint | Toolchain moderna |
| Interacción de Arrastre | Arrastre básico con mouse | Estilo arcball + inercia | Sensación más natural |
| Soporte Táctil | No explícito | Táctil completo + CSS móvil | Listo para móviles |
7. Guía de Uso Práctica
7.1 Nube Básica de Solo Texto
import { TagCloud } from "@xingwangzhe/tags-cloud";
const container = document.getElementById("cloud")!;new TagCloud(container, { tags: ["TypeScript", "React", "Vue", "Svelte", "Node.js", "Bun", "Rust"], radius: 250, spinY: 0.12, color: "#e0e0e0", fontSize: 16,});7.2 Nube Multi-Modal
new TagCloud(container, { tags: [ "TypeScript", { type: "image", src: "/avatar.webp", width: 40, height: 40 }, { type: "svg", content: `<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/></svg>`, width: 48, height: 48 }, { type: "video", src: "/demo.mp4", width: 120, height: 68 }, ], radius: 300, spinY: 0.15, onTagClick(item) { console.log("Clicked:", item); },});7.3 Gestión del Ciclo de Vida
const cloud = new TagCloud(container, { tags, spinY: 0.2 });cloud.setTags(["New", "Set", "Of", "Tags"]);cloud.pause();cloud.resume();cloud.destroy();8. Conclusión: Las Matemáticas como Motor de Renderizado
@xingwangzhe/tags-cloud se presenta como una demostración convincente de que el motor de renderizado más poderoso en el software no es una GPU, un compilador de shaders o un framework — son las matemáticas. Al descomponer el problema de la visualización de nubes de etiquetas 3D en tres operaciones matemáticas puras — distribución de esfera de Fibonacci, multiplicación de matrices de rotación y proyección perspectiva — la librería logra resultados visualmente indistinguibles de alternativas basadas en WebGL sin requerir ninguna de sus complejidades, dependencias de hardware o peso de bundle.
Las contribuciones del proyecto al ecosistema de código abierto son triples. Arquitectónicamente, demuestra que un renderizador híbrido Canvas/DOM puede ofrecer tanto rendimiento como versatilidad dentro de un paquete de 3KB. Matemáticamente, proporciona implementaciones limpias y bien documentadas de algoritmos fundamentales de gráficos 3D. Prácticamente, ofrece a los desarrolladores un componente listo para usar para crear nubes de etiquetas 3D con soporte de medios enriquecidos, seguridad de tipos de TypeScript e interacción preparada para móviles — todo sin agregar una sola dependencia.
Fin del Artículo — Basado en el análisis del código fuente de @xingwangzhe/tags-cloud v0.9.0