@xingwangzhe/tags-cloud: Мультимодальный движок 3D-облака тегов на чистой математике
@xingwangzhe/tags-cloud: Мультимодальный движок 3D-облака тегов на чистой математике
GitHub: xingwangzhe/tags-cloud · Демо: tagscloud.needhelp.icu · npm: @xingwangzhe/tags-cloud · Лицензия: MIT · Версия: v0.9.0
1. Введение: Переосмысление 3D-облака тегов для современного веба
Облако тегов остаётся одним из основных элементов веб-визуализации уже более двух десятилетий, эволюционировав от простых взвешенных списков слов к иммерсивным трёхмерным интерактивным пространственным скульптурам. Среди наиболее значимых вкладов в эту эволюцию — cong-min/TagCloud, лёгкая JavaScript-библиотека, показавшая, что сферу из вращающихся текстовых меток можно реализовать с минимальными затратами и без внешних зависимостей. Имея 390 звёзд и 93 форка на GitHub, оригинальный TagCloud заложил паттерн, который бесчисленные разработчики адаптировали для портфолио, демонстрации навыков и панелей визуализации данных. Его ключевое новшество заключалось в доказательстве того, что 3D-облака тегов не обязаны полагаться на тяжёлые WebGL-фреймворки или внешние движки рендеринга; вместо этого прямые тригонометрические расчёты в сочетании с аккуратным DOM-позиционированием могут давать удивительно впечатляющие визуальные результаты.
Встречайте @xingwangzhe/tags-cloud — выполненную с нуля переработку концепции 3D-облака тегов, сохраняющую математическую элегантность предшественника, но радикально расширяющую его возможности, модернизирующую архитектуру и раздвигающую границы того, что может рендерить облако тегов. Это не просто форк или добавление функциональности; это фундаментальный архитектурный сдвиг от текстового, тяжеловесного DOM-рендерера к мультимодальному движку, способному отображать текст, изображения, SVG-графику, произвольный HTML, видео и даже полноценные веб-компоненты — все они вращаются вместе в математически точном трёхмерном пространстве. Библиотека достигает этой универсальности, сохраняя исключительно компактный размер — примерно 3KB в gzip, — не имея ни одной runtime-зависимости и будучи построенной на современном инструментарии: Bun в качестве JavaScript-среды, Vite в качестве сборщика и TypeScript для полной типобезопасности во всей кодовой базе.
Слоган проекта — «мультимодальное 3D-облако тегов · движок на чистой математике» — отражает его двойственную природу. С одной стороны, это практичная библиотека UI-компонентов, которую разработчики могут встраивать в любой веб-проект для создания визуально впечатляющих сфер из тегов. С другой стороны, это математический движок, разлагающий сложную задачу рендеринга 3D-облака тегов на три чистых, детерминированных и красиво разделимых геометрических операции: распределение по сфере Фибоначчи для начального позиционирования, умножение матриц поворота для пространственных трансформаций и перспективная проекция для depth-осознанного 2D-рендеринга. Каждая из этих операций реализована в собственном модуле внутри директории src/core/, что делает кодовую базу не только хорошо поддерживаемой, но и отличным образовательным ресурсом для всех, кто хочет понять математику 3D-компьютерной графики без усложняющей сложности полного конвейера рендеринга.
Следующие разделы представляют всестороннее техническое погружение в каждый аспект @xingwangzhe/tags-cloud: от алгоритма распределения по сфере на основе золотого сечения до вдохновлённой кватернионами arcball-модели взаимодействия, от двойной архитектуры Canvas/DOM до адаптивного дизайна, обеспечивающего бесшовную работу на десктопах и мобильных устройствах. Будь вы разработчиком, оценивающим эту библиотеку для своего следующего проекта, графическим программистом, интересующимся лёгкими 3D-техниками, или математиком, любопытствующим о реальных применениях решётки Фибоначчи, — эта статья предлагает подробные объяснения, математические выводы, архитектурные схемы и аннотированные примеры кода, освещающие каждую грань этого замечательного программного продукта.
2. Математический фундамент: Три столпа 3D-рендеринга
В основе @xingwangzhe/tags-cloud лежит трио математических операций, которые в композиции превращают абстрактный список элементов контента в визуально связную вращающуюся сферу. Эти операции — распределение, поворот и проекция — реализованы в трёх модулях внутри src/core/, каждый отвечает за одно геометрическое преобразование. Такое разделение ответственности — не просто архитектурное предпочтение; оно отражает фундаментальную структуру конвейеров 3D-компьютерной графики, где генерация вершин, трансформация модели и камерная проекция являются отдельными стадиями, которые можно независимо оптимизировать, тестировать и понимать.
flowchart LR
subgraph Input["📥 Входной слой"]
TAGS["TagItem[]
(text | image | SVG | HTML | video | element)"]
OPTS["TagCloudOptions
(radius, spin, colors, callbacks)"]
end
subgraph CoreEngine["⚙️ Чистый математический движок (src/core/)"]
direction TB
DIST["🔵 distribution.ts
Сфера Фибоначчи
N точек → 3D координаты"]
ROT["🟢 rotation.ts
Матрицы поворота
3D точки → повёрнутые 3D точки"]
PROJ["🔴 projection.ts
Перспективная проекция
повёрнутые 3D → 2D + глубина"]
end
subgraph RenderLayer["🎨 Слой рендеринга"]
direction TB
CANVAS["Canvas 2D API
(текст + изображения)"]
DOM["DOM-оверлей
(SVG + HTML + видео + элементы)"]
end
subgraph Interaction["👤 Слой взаимодействия"]
MOUSE["Мышь / Сенсорное перетаскивание"]
INERTIA["Затухание инерции
(0.96/кадр)"]
end
TAGS --> DIST
OPTS --> DIST
DIST -->|"Vec3[]"| ROT
ROT -->|"Повёрнутые Vec3[]"| PROJ
PROJ -->|"ProjectedTag[]
(x, y, z, scale, alpha)"| CANVAS
PROJ --> DOM
MOUSE --> INERTIA
INERTIA -->|"углы поворота"| 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 Распределение Фибоначчи по сфере: Золотая геометрия размещения точек
Первый и, пожалуй, наиболее эстетически критичный шаг в создании 3D-облака тегов — определение позиции каждой метки на сфере. Наивный подход — размещать метки через равные интервалы по широте и долготе — неизбежно приводит к кластеризации у полюсов, где сходятся линии меридианов, и разреженным областям у экватора. Алгоритм сферы Фибоначчи элегантно решает эту проблему, создавая распределение точек, которое remarkably равномерно по всей поверхности сферы, без видимой кластеризации или пробелов независимо от количества точек.
Своё название алгоритм получил от золотого сечения (\varphi = \frac{1 + \sqrt{5}}{2} \approx 1.6180339887) — математической константы, повсеместно встречающейся в природе: от расположения семян подсолнуха до спиралей раковины наутилуса. Связь с числами Фибоначчи — последовательностью, где каждый член равен сумме двух предыдущих, — в том, что отношение последовательных чисел Фибоначчи сходится к золотому сечению по мере продвижения по ряду. В контексте распределения по сфере иррациональность золотого сечения гарантирует, что последовательные точки всегда размещаются под неповторяющимися углами, создавая апериодический спиральный паттерн, равномерно заполняющий поверхность сферы.
Конкретная реализация в src/core/distribution.ts использует вариацию решётки Фибоначчи, оптимизированную для сферического отображения. Для сферы радиуса (R) с (N) точками, которые нужно распределить, алгоритм вычисляет сферические координаты, а затем преобразует их в декартовы координаты ((x, y, z)) для каждой точки (i \in {0, 1, 2, \ldots, N-1}). Угол широты (\phi) (отсчитываемый от положительной оси z, то есть от Северного полюса) вычисляется через арккосинус для обеспечения равномерного распределения вдоль оси z:
Эта формула помещает первую точку чуть ниже Северного полюса, а последнюю — чуть выше Южного, причём сдвиг (+0.5) (заложенный в числителе (2i + 1)) гарантирует, что ни одна точка не окажется точно на полюсе. Это тонкое adjustment критически важно, поскольку предотвращает визуальный артефакт, когда метка находится прямо на верхушке или дне сферы, где вращение было бы почти незаметно. Функция (\arccos) преобразует равномерно распределённый линейный параметр в косинусно-взвешенное угловое распределение, компенсируя тот факт, что у сферы меньше площади поверхности у полюсов и больше — у экватора.
Угол долготы (\theta) (измеряемый в плоскости xy от положительной оси x) вычисляется масштабированием (\phi) на (\sqrt{N\pi}), что создаёт характерный спиральный паттерн:
Коэффициент (\sqrt{N\pi}) выводится из соотношения между золотым углом и общей площадью поверхности сферы. С ростом (N) это масштабирование гарантирует, что спираль оборачивается вокруг сферы нужное количество раз для сохранения равномерных промежутков между соседними точками. В результате каждый следующий поворот точки относительно предыдущего составляет примерно золотой угол (\approx 137.5°) при наблюдении вдоль оси z, что повторяет филлотактические спирали, встречающиеся в природе.
Наконец, эти сферические координаты преобразуются в декартовы по стандартным формулам:
Полученные точки удовлетворяют уравнению сферы (x_i^2 + y_i^2 + z_i^2 = R^2) и распределены с равномерностью, которая превосходит гораздо более вычислительно затратные методы на основе оптимизации. Следующая выносная формула суммирует полный алгоритм сферы Фибоначчи:
Реализация на TypeScript в src/core/distribution.ts remarkably лаконична, отражая элегантную простоту алгоритма:
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;}Эта функция возвращает массив объектов Vec3, которые служат начальными позициями для всех меток в облаке. Алгоритм работает за время (\mathcal{O}(N)), что делает его пригодным для облаков с сотнями элементов. На практике большинство облаков тегов содержат от 20 до 100 меток, для которых этот расчёт практически мгновенен.
2.2 3D-поворот: Умножение матриц в пространственной области
После того как метки размещены на поверхности сферы, следующий шаг — обеспечить вращение: как непрерывное автоматическое, так и управляемое пользователем через перетаскивание. Библиотека реализует вращение с помощью композитных матриц поворота, выровненных по осям, а именно — последовательных поворотов вокруг оси Y, а затем вокруг оси X. Этот подход вычислительно эффективен, прост для понимания и полностью достаточен для случая облака тегов, где вращения всегда выполняются относительно сферы с центром в начале координат.
Математическая основа этих вращений — линейная алгебра. Матрица поворота в 3D — это ортогональная матрица (3 \times 3) с определителем 1, которая преобразует координаты точки, сохраняя её расстояние от начала координат. Для поворота на угол (\alpha) вокруг оси Y матрица поворота (R_y(\alpha)) имеет вид:
Для поворота на угол (\beta) вокруг оси X матрица поворота (R_x(\beta)) имеет вид:
Библиотека применяет эти повороты последовательно: сначала вокруг оси Y, затем вокруг оси X. Для точки (P = (x, y, z)) поворот вокруг оси Y даёт промежуточную точку (P’):
Затем к (P’) применяется поворот вокруг оси X, дающий итоговую повёрнутую точку (P’’):
Реализация в src/core/rotation.ts разворачивает эти матричные умножения в прямые арифметические операции, избегая накладных расходов на выделение объектов матриц и умножение. Для одной точки функция поворота вычисляет:
Функция rotatePoints выполняет это преобразование пакетно для всех меток:
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 Перспективная проекция: Из 3D-пространства на 2D-экран
Финальная математическая операция преобразует повёрнутые 3D-координаты в 2D-позиции на экране. Библиотека использует упрощённую, но весьма эффективную модель проекции, которая вычисляет коэффициент масштаба и коэффициент непрозрачности на основе глубины, с параметром перспективной глубины, равным удвоенному радиусу сферы:
Коэффициент непрозрачности (alpha) использует квадратичную зависимость с корректировкой смещения:
Полный конвейер проекции суммируется как:
Реализация на TypeScript в точности отражает эту математику:
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. Архитектура и дизайн модулей
Кодовая база является примером чистой программной архитектуры благодаря продуманному разделению математических вычислений, оркестрации рендеринга и обработки пользовательского взаимодействия.
3.1 Чистый математический движок: src/core/
| Модуль | Функция | Вход | Выход | Ключевая формула |
|---|---|---|---|---|
| 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 Двойная система рендеринга: Canvas + DOM
Одно из наиболее значительных нововведений — это двойная система рендеринга, использующая Canvas 2D API для текста и изображений и DOM-оверлей для SVG, HTML, видео и веб-компонентов.
flowchart TB
subgraph "Типы тегов и рендереры"
direction LR
TEXT["📄 Текстовый тег"]
IMG["🖼️ Тег-изображение"]
SVG_TAG["🎨 SVG-тег"]
HTML_TAG["🌐 HTML-тег"]
VIDEO["🎬 Видео-тег"]
ELEMENT["🔧 Тег-элемент"]
end
subgraph "Выбор рендерера"
CANVAS["Canvas 2D API
✅ Текст
✅ Изображения"]
DOM["DOM-оверлей
✅ SVG
✅ HTML
✅ Видео
✅ Веб-компоненты"]
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. Характеристики производительности
4.1 Вычислительная сложность
| Стадия | Сложность | Доминирующие операции |
|---|---|---|
| Поворот | O(N) | 8 умножений + 4 сложения на точку |
| Проекция | O(N) | 1 деление + 2 умножения + 1 сложение на точку |
| Z-сортировка | O(N log N) | Сортировка сравнением |
| Canvas-рендеринг | O(N) | fillText / drawImage вызовы |
| DOM-обновление | O(N) | transform + opacity установка |
4.2 Эффективность использования памяти
Для (N = 100) меток общее числовое состояние составляет около (100 \times (3 + 5) \times 8 = 6,400) байт — менее 7KB чисел с плавающей точкой.
4.3 Размер бандла
| Формат | Размер |
|---|---|
| ESM (dist/index.js) | ~12 KB |
| Gzip | ~3 KB |
| Brotli | ~2.5 KB |
5. Мультимодальный контент: Шесть типов тегов
| Тип тега | Формат ввода | Рендерер | Интерактивность | Сценарий использования |
|---|---|---|---|---|
| Текст | string | Canvas 2D | колбэк onTagClick | Навыки, ключевые слова |
| Изображение | { type:“image”, src, w, h } | Canvas 2D | onClick на тег | Логотипы, аватары |
| SVG | { type:“svg”, content, w, h } | DOM | onClick на тег | Векторные иконки |
| HTML | { type:“html”, html } | DOM | onClick на тег | Форматированный текст |
| Видео | { type:“video”, src, w, h } | DOM | Полный экран по клику | Демо-клипы |
| Элемент | { type:“element”, element } | DOM | Нативные DOM-события | Веб-компоненты |
6. Сравнение с cong-min/TagCloud
| Аспект | cong-min/TagCloud | @xingwangzhe/tags-cloud | Улучшение |
|---|---|---|---|
| Язык | JavaScript (ES5) | TypeScript | Полная типобезопасность |
| Размер бандла | ~6KB минифицировано | ~3KB gzip | ~на 50% меньше |
| Зависимости | 0 | 0 | Оба без зависимостей |
| Типы тегов | Только текст | Текст, Изображение, SVG, HTML, Видео, Элемент | 6x типов контента |
| Рендерер | Только DOM | Canvas 2D + DOM гибрид | Лучшая производительность |
| Инструмент сборки | Rollup + Babel | Vite + Bun + Oxlint | Современный инструментарий |
| Drag-взаимодействие | Базовое мышью | Arcball-стиль + инерция | Более естественное ощущение |
| Поддержка сенсорного ввода | Неявная | Полная поддержка + мобильный CSS | Готово для мобильных |
7. Практическое руководство
7.1 Базовое текстовое облако
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 Мультимодальное облако
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 Управление жизненным циклом
const cloud = new TagCloud(container, { tags, spinY: 0.2 });cloud.setTags(["New", "Set", "Of", "Tags"]);cloud.pause();cloud.resume();cloud.destroy();8. Заключение: Математика как движок рендеринга
@xingwangzhe/tags-cloud служит убедительной демонстрацией того, что самый мощный движок рендеринга в программировании — это не GPU, не компилятор шейдеров и не фреймворк, а математика. Разложив задачу визуализации 3D-облака тегов на три чистых математических операции — распределение Фибоначчи по сфере, умножение матриц поворота и перспективную проекцию, — библиотека достигает результатов, визуально неотличимых от WebGL-альтернатив, не требуя при этом ни их сложности, ни аппаратных зависимостей, ни веса бандла.
Вклад проекта в open-source экосистему троекратен. Архитектурно он доказывает, что гибридный Canvas/DOM-рендерер может обеспечивать как производительность, так и универсальность в 3KB-пакете. Математически он предлагает чистые, хорошо документированные реализации фундаментальных алгоритмов 3D-графики. Практически он даёт разработчикам готовый компонент для создания 3D-облаков тегов с поддержкой мультимедиа, типобезопасностью TypeScript и мобильной интерактивностью — и всё это без добавления единой зависимости.
Конец статьи — Основано на анализе исходного кода @xingwangzhe/tags-cloud v0.9.0