Связи (pixel-aquarium): Умный фокус (Smart Zoom / «аквариум») — погружение в узел + LOD
Новая ветка для безопасного отката (от pixel-web). Режим «Вселенная», только лаборатория.
1. Гибрид клика: 1-й уровень → раскрытие ветки НА МЕСТЕ (как раньше); 2-й уровень+ → ПОГРУЖЕНИЕ.
2. Dive (умный наезд камеры, «аквариум», без перестройки графа):
- diveTo(node): пинит весь путь (предки до Ивана), ставит diveTargetId + diveZoom=1.7;
камера в tick плавно ЛЕТИТ и ЗУМИТ, центрируя узел (DIVE_FLY_K), узел ВЫРАСТАЕТ (×2.1 ~ герой).
- Глубина (contextTargetOf → depthScale/depthBlur/spotCur, лерп): Иван и боковые ветки
УМЕНЬШАЮТСЯ (root ×0.55) + уходят в BLUR 3px + тускнеют до 0.25 → задний план «аквариума».
- Нить-крошка: путь Иван→…→узел (divePathSet/onPath) горит ярким «световодом» — виден путь назад.
- Всплытие: повтор клика по цели → exitDive (камера/зум плавно к корню); клик по Ивану →
collapseAll (полный сброс + всплытие).
3. Pinch-to-Zoom + LOD 3-го уровня: при zoom≥1.55 видимые точки 3-го уровня дорисовываются как
читаемые аватарки (лицо+имя; updateLod/setNodeLod — пере-рендер DOM на пороге), при отдалении —
обратно в светящиеся точки. Узлам tier-3 добавлены фото-заглушки (pravatar) и имена.
Глубина — фейк-3D через масштаб + CSS-blur (GPU), без WebGL. Реальный путь /network-view не затронут:
dive только tier≥2 (в реале их нет), depthScale/Blur нейтральны по умолчанию, updateLod выходит при !hasDeep.
Бамп client.version → 1.2.146.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f92e6c3cf1
commit
7a8852f64b
@ -1,2 +1,2 @@
|
||||
client.version=1.2.145
|
||||
server.version=1.2.129
|
||||
client.version=1.2.146
|
||||
server.version=1.2.130
|
||||
|
||||
@ -113,8 +113,24 @@
|
||||
толщиной/размытием 3.6с — в такт ободку сияющего узла (в покое SVG не перерисовывается → синхронно).
|
||||
- Мерцающие микрозвёзды 3-го уровня (`fg-star-twinkle`), хаптика (`navigator.vibrate`) на нажатие/раскрытие/натяжение.
|
||||
|
||||
> ⚠️ Эксперимент на ветке `pixel-web` (для отката). Реальный путь `/network-view` не затронут:
|
||||
> весь deep-код под `tier ≥ 2` / `hasDeep`, hover-колбэк передаёт только лаборатория.
|
||||
### Умный фокус (Smart Zoom / «аквариум») — ветка `pixel-aquarium`
|
||||
Клик по узлу разный по уровню (гибрид): **1-й уровень** — раскрытие ветки НА МЕСТЕ (как выше);
|
||||
**2-й уровень+** — **погружение (dive)**:
|
||||
- **Камера-полёт + зум** (`diveTo` → `diveTargetId`/`diveZoom=1.7`, лёт в `tick` с `DIVE_FLY_K`): узел
|
||||
плавно центрируется и **вырастает** (`DIVE_TARGET_MUL=2.1` → ~герой-размер).
|
||||
- **Глубина «аквариума»** (`contextTargetOf` → `depthScale`/`depthBlur`/`spotCur`, лерп): Иван и боковые
|
||||
ветки **уменьшаются** (root ×0.55, фон ×0.55) + уходят в **blur 3px** + тускнеют до 0.25 → задний план.
|
||||
- **Нить-крошка**: путь Иван → … → узел (`divePathSet`/`onPath`) горит ярким «световодом» — видно путь назад.
|
||||
- **Всплытие**: повторный клик по узлу-цели → `exitDive` (камера/зум плавно возвращаются к корню);
|
||||
клик по Ивану → `collapseAll` (полный сброс + всплытие).
|
||||
- **Pinch-to-Zoom + LOD**: щипок/колесо меняют `zoom`; при `zoom ≥ LOD_ZOOM (1.55)` видимые точки 3-го
|
||||
уровня **дорисовываются как аватарки** (лицо+имя, `updateLod`/`setNodeLod` — пере-рендер DOM на пороге),
|
||||
при отдалении сворачиваются обратно в светящиеся точки.
|
||||
- Глубина — фейк-3D через масштаб + CSS-`blur` (GPU), без WebGL.
|
||||
|
||||
> ⚠️ Эксперименты на ветках `pixel-web` (паутина) и `pixel-aquarium` (Smart Zoom) — для отката.
|
||||
> Реальный путь `/network-view` не затронут: deep-код под `tier ≥ 2` / `hasDeep`, dive — только tier≥2
|
||||
> (в реальном пути их нет), depthScale/Blur по умолчанию нейтральны, `updateLod` выходит при `!hasDeep`.
|
||||
|
||||
## Ограничения / на будущее
|
||||
- Многоуровневая глубина (друзья друзей мельче, 3-й уровень — точки), кластеры, «общие связи»
|
||||
|
||||
@ -63,6 +63,18 @@ const SPOTLIGHT_DIM = 0.25; // прозрачность «затемнённ
|
||||
const CAM_GLIDE_K = 0.12; // скорость дотяжки (lerp за кадр)
|
||||
const CAM_GLIDE_MARGIN = 18; // зазор от края экрана при дотяжке, px
|
||||
|
||||
// Умный фокус (Smart Zoom / «аквариум»): клик по узлу 2-го уровня — камера летит и зумит к нему,
|
||||
// он вырастает в центр; Иван и боковые ветки уменьшаются + расфокус (blur) на задний план;
|
||||
// нить-крошка обратно к Ивану остаётся яркой. При сильном зуме точки 3-го уровня → аватарки (LOD).
|
||||
const DIVE_ZOOM = 1.7; // зум камеры при погружении
|
||||
const DIVE_FLY_K = 0.13; // скорость «полёта» камеры/зума к узлу (lerp за кадр)
|
||||
const DIVE_TARGET_MUL = 2.1; // множитель масштаба «нырнутого» узла (вырастает в герой-центр)
|
||||
const DIVE_PATH_MUL = 0.72; // предки на пути назад — чуть мельче (видимая «цепочка крошек»)
|
||||
const DIVE_ROOT_MUL = 0.55; // корень (Иван) уходит вглубь сильнее всех
|
||||
const DIVE_OFFPATH_MUL = 0.55; // боковые ветки (вне пути) — уменьшаются на задний план
|
||||
const DIVE_BLUR = 3; // размытие фоновых (вне пути) узлов — эффект расфокуса/глубины, px
|
||||
const LOD_ZOOM = 1.55; // порог зума, на котором точки 3-го уровня превращаются в аватарки
|
||||
|
||||
const RELATION_COLORS = {
|
||||
family: 'rgba(255, 159, 94, 0.92)',
|
||||
friend: 'rgba(120, 179, 255, 0.9)',
|
||||
@ -152,6 +164,9 @@ export function createForceGraph({ stage, model, onCenterTap, onNodeTap, onNodeL
|
||||
let nodeById = new Map(); // id → node (для поиска родителя у глубоких уровней)
|
||||
let hasDeep = false; // есть ли в графе узлы tier≥2 (включает deep-ветки рендера/раскладки)
|
||||
let spotActive = false; // активен ли «spotlight» (есть закреплённая ветка → остальные тускнеют)
|
||||
let diveTargetId = null; // id «нырнутого» узла (Smart Zoom); null — мы на верхнем уровне (Иван)
|
||||
let diveZoom = 1; // целевой зум активного погружения
|
||||
let surfacing = false; // идёт «всплытие» назад (камера/зум возвращаются к корню)
|
||||
const rebuildIndex = () => { nodeById = new Map(nodes.map((n) => [String(n.id), n])); hasDeep = nodes.some((n) => n.tier >= 2); };
|
||||
|
||||
// Spotlight: при закреплённой кликом ветке остальной граф тускнеет до SPOTLIGHT_DIM (0.25), чтобы
|
||||
@ -169,6 +184,36 @@ export function createForceGraph({ stage, model, onCenterTap, onNodeTap, onNodeL
|
||||
return (r.pinned || r.hovered) ? 1 : SPOTLIGHT_DIM;
|
||||
}
|
||||
|
||||
// Путь-«крошки» от корня (Иван) до нырнутого узла включительно — для подсветки нити назад и глубины.
|
||||
function divePathSet() {
|
||||
const set = new Set();
|
||||
if (!diveTargetId) return set;
|
||||
let cur = nodeById.get(diveTargetId); let guard = 0;
|
||||
while (cur && guard++ < 16) { set.add(String(cur.id)); if (cur.isFocus) break; const p = nodeById.get(cur.parentId); if (!p) break; cur = p; }
|
||||
set.add(String(focusId));
|
||||
return set;
|
||||
}
|
||||
let _pathSet = new Set();
|
||||
let _pathSetKey = ' | ||||