SHiNE-server/shine-UI/Dev_Docs/features/interactive-network-graph.md
Pixel e0f0726e68 Связи: интерактивная карта связей (force-directed graph)
Переработка экрана «Связи» в интерактивный нод-граф с премиальными переходами.

Движок (js/pages/network/force-graph.js):
- diffing-переходы: общие узлы перелетают, новые расцветают каскадом, исчезнувшие — Ghost-слой (800мс, на месте);
- мягкая радиальная пружина + отталкивание (органичная орбита), упругий влёт фокуса;
- динамическая вязкость на старте (трение 0.92→0.82, отталкивание ослаблено) — мягкий разлёт без тряски;
- жёсткая заморозка (kill-switch) при затухании — нет «треска», экономия батареи;
- линии — SVG <path> Безье (изогнутые нити), прорастание; жесты pan с инерцией;
- хард-лимит DOM-аватарок (остальное — SVG-точки).

Интеграция и UX:
- adapter.js: getUserConnectionsGraph → модель движка (сервер не трогаем, read-only);
- фильтры (Все/Семья/Друзья/Сияющие), контекстное меню (node-menu.js), нижний сниппет, профиль;
- прицел в центре, дыхание фокуса, свечение сияющих;
- лаборатория network-view/lab на мок-данных (networkGraphUsers) для тестов без бэкенда.

Документация: shine-UI/Dev_Docs/features/interactive-network-graph.md.
Бамп client.version 1.2.135 -> 1.2.136.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 21:23:16 +03:00

73 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Интерактивная карта связей (force-directed graph)
Экран **«Связи»** (`network-view`) — интерактивная нод-граф карта вместо статичного списка:
фокусный пользователь в центре, связи на орбите, навигация тапом/свайпом, премиальные
переходы в духе нативного iOS.
## Где код
- `js/pages/network/force-graph.js`**движок** (физика, рендер, жизненный цикл узлов, жесты).
- `js/pages/network/adapter.js` — реальные данные → нейтральная модель движка.
- `js/pages/network/node-menu.js` — общее контекстное меню узла.
- `js/pages/network/lab.js` — лаборатория (`network-view/lab`) на мок-данных, без бэкенда.
- `js/pages/network-view.js` — страница: шапка, поиск, фильтры, история, склейка с движком.
- `js/mock-data.js``networkGraphUsers` (связанный мульти-граф из 10 человек для лаборатории).
- `styles/network-graph.css` — все стили `.fg-*`.
## Данные (read-only, сервер не трогаем)
Единый источник — `authService.getUserConnectionsGraph(login)` (один запрос: логин → прямые связи).
`network-view.js``buildGraphModel()` нормализует роли (parent/child/sibling/spouse/friend/contact),
направление и метки; `adapter.engineModelFromGraphModel()` превращает это в модель движка:
`{ focusId, nodes:[{ id, login, name, avatar, relationType, strength, shining, tier }] }`.
## Модель движка и API
`createForceGraph({ stage, model, onNodeTap, onCenterTap, onNodeLongPress })`
`{ setModel(model), setFilter(pred), recenter(id), getFocusNode(), destroy() }`.
## Ключевые механики
- **Diffing-переходы (непрерывность состояний):** при смене фокуса общие узлы (тот же `id`) не
пересоздаются, а перелетают пружиной на новые места; новые «расцветают» (bloom) каскадом из центра;
исчезнувшие уходят в Ghost-слой.
- **Ghost-слой:** снимок всего старого графа (узлы + линии) на полноэкранном overlay, застывает
на месте, `scale 1→0.7` + `opacity 0.5→0` за **800мс**, затем удаляется (красивый шлейф истории).
- **Физика:** мягкая радиальная пружина к орбите + взаимное отталкивание (charge) → органичная,
слегка неровная орбита; фокус влетает в центр упруго. Координаты узлов на трансформах (GPU).
- **Каскадный bloom:** новые узлы скрыты в центре и «выстреливают» по очереди (`order × 40мс`).
- **Динамическая вязкость:** первые ~600мс после перестроения трение завышено (0.92), отталкивание
ослаблено (×0.45) → гасит «взрыв», затем плавно к базе (0.82) — мягкое «резиновое» появление.
- **Жёсткая заморозка (kill-switch):** когда сумма |vx|+|vy| < 0.03 скорости обнуляются, координаты
округляются до целых пикселей, `cancelAnimationFrame` (sleep). Нет «треска», батарея не страдает.
- **Линии:** SVG `<path> Q` (квадратичные Безье) изящные изогнутые нити, тонкие/полупрозрачные;
при движении изгиб реагирует на скорость; новые линии прорастают (`stroke-dashoffset`).
- **Жесты:** свайп-pan с инерцией (новое касание прерывает); короткий тап центрирование + нижний
сниппет; долгий тап контекстное меню (в `#modal-root`, позиция по `getBoundingClientRect`); тап
по центру профиль.
- **Фильтры слоёв:** Все / Семья / Друзья / Сияющие (плавное скрытие/перераспределение).
- **Поллиш:** «прицел» в центре (+пульс при захвате фокуса), «дыхание» фокуса (бесконечная CSS-анимация
размера 1.481.52x, GPU, не будит rAF), свечение «сияющих», хард-лимит ~90 DOM-аватарок (остальное
SVG-точки).
## Параметры тюнинга (константы в начале `force-graph.js`)
| Константа | Значение | Назначение |
|---|---|---|
| `ORBIT_MIN / ORBIT_MAX` | 150 / 240 | радиус орбиты (защитный отступ от центра подписи не наезжают) |
| `K_RADIAL` | 0.035 | жёсткость орбитальной пружины (мягко) |
| `K_FOCUS` | 0.12 | жёсткость пружины фокуса к центру |
| `CHARGE` / `CHARGE_START_FACTOR` | 1400 / 0.45 | отталкивание (на старте ослаблено) |
| `FRICTION` / `FRICTION_BOOST` / `BOOST_FRAMES` | 0.82 / 0.92 / 36 | базовое трение / стартовая вязкость / длительность (~600мс) |
| `SLEEP_V` | 0.03 | порог суммарной |v| для заморозки |
| `FOCUS_SCALE` | 1.5 | базовый масштаб фокуса |
| `MAX_FULL_NODES` | 90 | хард-лимит полных аватарок (далее точки) |
## Локальный запуск / проверка
- Dev-сервер: `.claude/shine-ui-dev-server.cjs` (Node, порт 7321, SPA-fallback + инжект `<base href="/">`).
- Лаборатория (без бэкенда): `http://localhost:7321/network-view/lab` мок `networkGraphUsers`,
тап по узлам переключает сети.
- Реальный путь (`/network-view`) требует живого `wss://shineup.me/ws` (локально `ws_open_error`, это норма).
## Ограничения / на будущее
- Многоуровневая глубина (друзья друзей мельче, 3-й уровень точки), кластеры, «общие связи»
упираются в API (отдаёт только прямые связи) требуют доработки сервера.
- `lerpX/lerpY` в движке больше не используются для отрисовки кандидат на чистку.
- Превью в простое троттлит `requestAnimationFrame` (физика не идёт между вызовами) для замеров
прокачивать кадры; в активном табе всё работает на 60 FPS.