From dc96033cb1a16a49702cb886d832770b3609bb3ec4aba24244cfdd557910210a Mon Sep 17 00:00:00 2001 From: Pixel Date: Tue, 9 Jun 2026 21:23:17 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=B2=D1=8F=D0=B7=D0=B8:=20=D0=B4=D0=B2?= =?UTF-8?q?=D1=83=D1=85=D1=81=D0=BB=D0=BE=D0=B9=D0=BD=D1=8B=D0=B5=20=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=B8=D0=B8-=D1=81=D0=B2=D0=B5=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=D0=BE=D0=B4=D1=8B,=20=D0=B6=D0=B8=D0=B2=D0=BE=D0=B9=20=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=20=D0=B8=20=D1=81=D1=82=D0=B5=D0=BA=D0=BB=D1=8F?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D0=B5=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80?= =?UTF-8?q?=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Сияющие связи — двухслойный неоновый «световод»: размытый glow (4px, blur 2px, opacity 0.4) + тонкий чёткий core (1.5px, #e0f7fc). Объёмное OLED-свечение, линия остаётся изящной. Оба слоя растут синхронно (общий dashoffset). - Обычные линии — тоньше (1.0–1.2px) и глубокий уход в прозрачность (0.42 → 0.07), чтобы матовые связи не спорили с сияющими. - Живой фон-«небула»: глубокое размытое сине-голубое облако под центром, медленная пульсация радиуса/яркости + переливы индиго↔ультрамарин (hue-rotate, 7с). - Стеклянные чипы фильтров (frosted glass): rgba(255,255,255,0.03) + backdrop blur(12px) + граница 0.5px solid rgba(255,255,255,0.1); активный подсвечен сине-голубым. - Бамп client.version → 1.2.138; документация фичи обновлена. Co-Authored-By: Claude Opus 4.8 (1M context) --- VERSION.properties | 2 +- .../features/interactive-network-graph.md | 16 +++-- shine-UI/js/pages/network/force-graph.js | 48 +++++++------- shine-UI/styles/network-graph.css | 63 +++++++++++++++---- 4 files changed, 89 insertions(+), 40 deletions(-) diff --git a/VERSION.properties b/VERSION.properties index e03abab..c2ecf84 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.137 +client.version=1.2.138 server.version=1.2.127 diff --git a/shine-UI/Dev_Docs/features/interactive-network-graph.md b/shine-UI/Dev_Docs/features/interactive-network-graph.md index 85e7cb9..686fd10 100644 --- a/shine-UI/Dev_Docs/features/interactive-network-graph.md +++ b/shine-UI/Dev_Docs/features/interactive-network-graph.md @@ -42,10 +42,13 @@ органичного покачивания; после фильтра физика НЕ включается (фиксация на равномерных углах). - **Жёсткая заморозка (kill-switch):** когда сумма |vx|+|vy| < 0.03 — скорости обнуляются, координаты округляются, `cancelAnimationFrame` (sleep). Нет «треска», батарея не страдает. -- **Линии:** SVG ` Q` (квадратичные Безье) — тонкие изящные дуги (`stroke-width ~1.3–2.2`), - градиент неон-центр → цвет роли. Изгиб реагирует на скорость. -- **Сияющие связи:** линия к «сияющему» узлу — ярче (градиент в неон) и МОНОЛИТНО светится статичной - тенью `filter: drop-shadow(...)` (тот же приём, что у ободка аватарки). Без бегущих импульсов. +- **Обычные линии:** SVG ` Q` (квадратичные Безье) — тонкие матовые дуги (`stroke-width ~1.0–1.2`), + градиент с глубоким уходом в прозрачность: неон-центр `0.42` → цвет роли `0.07` у аватарки (растворяются + в фоне, не спорят с сияющими). Изгиб реагирует на скорость. +- **Сияющие связи (двухслойный «световод», Neon Layering):** два пути на одну связь — широкий размытый + GLOW (`stroke-width 4`, неон, `filter: blur(2px)`, `opacity 0.4`) + тонкий чёткий CORE (`1.5px`, + `#e0f7fc`). Линия остаётся изящной, но обретает объёмное OLED-свечение; растут оба синхронно (общий + dashoffset). Никаких бегущих импульсов. - **Жесты:** свайп-pan с инерцией (новое касание прерывает); короткий тап — центрирование + нижний сниппет; долгий тап — контекстное меню (в `#modal-root`, позиция по `getBoundingClientRect`); тап по центру — профиль. Нажатия на чипы фильтров гасят `pointerdown` (stopPropagation), чтобы сцена не @@ -53,6 +56,11 @@ - **Фильтры слоёв (Все / Семья / Друзья / Сияющие):** CSS-переходы 300мс — несоответствующие узлы и их линии гаснут НА МЕСТЕ (`opacity 0` + `scale 0.8`), оставшиеся плавно переплывают на равномерные углы, затем жёсткая фиксация без физики (ноль тряски, мгновенный sleep). +- **Живой фон (Nebula):** под центром — глубокое размытое сине-голубое облако (`.fg-stage::before`, + `blur 80→96px`), бесконечная анимация 7с: «дышит» радиусом/яркостью и переливается индиго↔ультрамарин + (`hue-rotate`). На компоновщике (GPU), не будит rAF; подписи имён остаются контрастными. +- **Стеклянные чипы фильтров (frosted glass):** `background: rgba(255,255,255,0.03)`, + `backdrop-filter: blur(12px)`, граница `0.5px solid rgba(255,255,255,0.1)`; активный — подсвечен сине-голубым. - **Поллиш:** «прицел» в центре (+пульс при захвате фокуса); «дыхание» фокуса (бесконечная CSS-анимация размера, GPU, не будит rAF); свечение «сияющих» узлов — мягкая медленная пульсация (3.6с) многослойной `box-shadow` + размытый ореол через SVG-фильтр `#fg-shine-glow`; тестовые фото-аватарки (`NETWORK_PHOTOS`); diff --git a/shine-UI/js/pages/network/force-graph.js b/shine-UI/js/pages/network/force-graph.js index 7faddbf..3adf2d5 100644 --- a/shine-UI/js/pages/network/force-graph.js +++ b/shine-UI/js/pages/network/force-graph.js @@ -53,8 +53,6 @@ const RELATION_COLORS = { // Неоновый цвет центра — из него «вытекает» градиент каждой связи к цвету периферийного узла. const FOCUS_NEON = 'rgba(140, 240, 255, 0.95)'; -// Яркий неон сияния — в него уходит градиент связи к «сияющему» узлу (совпадает со свечением аватарки). -const SHINE_EDGE_NEON = 'rgba(150, 245, 255, 0.95)'; function easeOutCubic(t) { const x = 1 - t; @@ -416,26 +414,14 @@ export function createForceGraph({ stage, model, onCenterTap, onNodeTap, onNodeL const desY = my + ux * baseBow + invY * lag; const cpx = 2 * desX - mx; // CP так, чтобы середина Q-кривой попала в desired const cpy = 2 * desY - my; - // ТОНКАЯ изящная дуга: одинарная квадратичная кривая Безье, лёгкий градиентный штрих. - // Обычная связь — неон у центра → цвет роли у узла. Связь к «СИЯЮЩЕМУ» — ярче, уходит в - // неон сияния и МОНОЛИТНО светится (статичный drop-shadow через класс .fg-edge-shine). + // Связь рисуем по статусу узла: + // • обычная — одна тонкая (1.0–1.2px) матовая дуга, градиент с ГЛУБОКИМ уходом в прозрачность; + // • СИЯЮЩАЯ — двухслойный неоновый «световод»: широкий размытый glow (под) + тонкий чёткий + // core 1.5px (над) → изящно, но с объёмным OLED-свечением (см. .fg-edge-glow / .fg-edge-core). const shine = Boolean(n.shining) && !n.hidden; - const gid = `fg-grad-${gi}`; - gi += 1; - const tipColor = shine ? SHINE_EDGE_NEON : relationColor(n.relationType); - const baseStop = shine ? 0.85 : 0.5; - const tipStop = shine ? 0.7 : 0.14; - defs.push( - `` - + `` - + `` - ); - const sw = (shine ? 1.7 + n.strength * 0.8 : 1.3 + n.strength * 0.9).toFixed(2); // тонко const d = `M${x1.toFixed(1)} ${y1.toFixed(1)} Q${cpx.toFixed(1)} ${cpy.toFixed(1)} ${x2.toFixed(1)} ${y2.toFixed(1)}`; - // прозрачность луча = живая прозрачность узла (гаснет вместе с узлом при фильтре/уходе) - const op = nodeOpacity < 0.995 ? ` opacity="${nodeOpacity.toFixed(2)}"` : ''; - // ПРОРАСТАНИЕ из центра: dasharray = длина пути, dashoffset уводим от длины к 0 по мере - // разлёта (growP = доля пройденного узлом пути центр→орбита) → линия «вытягивается» из центра. + // ПРОРАСТАНИЕ из центра: dasharray = длина пути, dashoffset от длины к 0 по мере разлёта узла + // (growP = доля пройденного узлом пути центр→орбита) → линия «вытягивается» из центра. let dashAttr = ''; if (growing) { const finalD = Math.hypot(n.bfx, n.bfy) || 1; @@ -444,8 +430,26 @@ export function createForceGraph({ stage, model, onCenterTap, onNodeTap, onNodeL const L = (Math.hypot(cpx - x1, cpy - y1) + Math.hypot(x2 - cpx, y2 - cpy) + Math.hypot(x2 - x1, y2 - y1)) / 2; dashAttr = ` stroke-dasharray="${L.toFixed(1)}" stroke-dashoffset="${(L * (1 - growP)).toFixed(1)}"`; } - const cls = shine ? ' class="fg-edge-shine"' : ''; // монолитное неоновое свечение (drop-shadow) - parts.push(``); + if (shine) { + // glow (размытый, приглушённый) + core (тонкий, чёткий) — растут синхронно (общий dashAttr) + const gOp = (0.4 * nodeOpacity).toFixed(2); + const cOpAttr = nodeOpacity < 0.995 ? ` opacity="${nodeOpacity.toFixed(2)}"` : ''; + parts.push(``); + parts.push(``); + } else { + // обычная: тонкая дуга, градиент 0.42 (центр) → 0.07 (аватарка) — глубокий уход в прозрачность, + // чтобы матовые связи не спорили с сияющими. + const gid = `fg-grad-${gi}`; + gi += 1; + defs.push( + `` + + `` + + `` + ); + const sw = (1.0 + n.strength * 0.2).toFixed(2); // 1.0–1.2px + const op = nodeOpacity < 0.995 ? ` opacity="${nodeOpacity.toFixed(2)}"` : ''; + parts.push(``); + } } edgesSvg.innerHTML = `${defs.join('')}${parts.join('')}`; } diff --git a/shine-UI/styles/network-graph.css b/shine-UI/styles/network-graph.css index c86277b..eaa552f 100644 --- a/shine-UI/styles/network-graph.css +++ b/shine-UI/styles/network-graph.css @@ -16,6 +16,29 @@ cursor: grabbing; } +/* Живой фон-«небула»: глубокое размытое сине-голубое облако света строго под центральным узлом. + Медленно «дышит» (радиус/яркость) и переливается индиго↔ультрамарин (hue-rotate) за 7с. + Чистый CSS на компоновщике — создаёт ощущение живой светящейся среды, не будит rAF-цикл. */ +.fg-stage::before { + content: ''; + position: absolute; + inset: 0; + background: radial-gradient(circle 320px at 50% 47%, rgba(80, 150, 255, 0.30) 0%, rgba(60, 100, 220, 0.15) 42%, rgba(40, 70, 170, 0) 72%); + filter: blur(80px); + pointer-events: none; + z-index: 0; /* строго под линиями (z:0, но раньше по порядку) и узлами (z:1) */ + animation: fg-nebula 7s ease-in-out infinite; +} + +@keyframes fg-nebula { + 0%, 100% { opacity: 0.70; filter: blur(80px) hue-rotate(-12deg); } + 50% { opacity: 1.00; filter: blur(96px) hue-rotate(16deg); } +} + +@media (prefers-reduced-motion: reduce) { + .fg-stage::before { animation: none; } +} + .fg-world { position: absolute; left: 50%; @@ -37,10 +60,20 @@ transition: opacity 420ms ease; /* плавное появление линий при перестройке */ } -/* Связь к «сияющему» узлу — МОНОЛИТНО светится мягким неоном (тот же приём, что у ободка аватарки): - статичная двухслойная тень через drop-shadow. Никакой динамики, бегущих точек и пульсаций. */ -.fg-edge-shine { - filter: drop-shadow(0 0 3px rgba(140, 235, 255, 0.75)) drop-shadow(0 0 6px rgba(110, 225, 255, 0.4)); +/* Сияющая связь = двухслойный неоновый «световод» (Neon Layering): изящно, но объёмно (как OLED). + GLOW — широкий размытый ореол неонового оттенка под линией; CORE — тонкий чёткий светлый контур. */ +.fg-edge-glow { + fill: none; + stroke: rgba(110, 225, 255, 1); + stroke-width: 4; + stroke-linecap: round; + filter: blur(2px); /* мягкое объёмное свечение вокруг нити */ +} +.fg-edge-core { + fill: none; + stroke: #e0f7fc; /* ультра-светлый голубой — чёткий контур луча */ + stroke-width: 1.5; + stroke-linecap: round; } .fg-node { @@ -294,26 +327,30 @@ pointer-events: none; } +/* Стеклянные табы — тонкие пластины матового стекла (frosted glass) */ .fg-filter-chip { pointer-events: auto; - border: 1px solid rgba(166, 196, 245, 0.28); - background: rgba(10, 20, 37, 0.6); - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); + border: 0.5px solid rgba(255, 255, 255, 0.1); + background: rgba(255, 255, 255, 0.03); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); color: #cfe0ff; font-size: 12px; font-weight: 600; line-height: 1; - padding: 7px 13px; + padding: 7px 14px; border-radius: 999px; cursor: pointer; - transition: background 140ms ease, border-color 140ms ease, color 140ms ease; + box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.08); /* лёгкий стеклянный блик сверху */ + transition: background 160ms ease, border-color 160ms ease, color 160ms ease, box-shadow 160ms ease; } +/* Активный таб — то же стекло, но подсвеченное сине-голубым (в тон неону графа) */ .fg-filter-chip.is-active { - background: linear-gradient(130deg, rgba(61, 196, 223, 0.92), rgba(58, 95, 142, 0.92)); - border-color: rgba(180, 230, 255, 0.85); - color: #061119; + background: rgba(125, 215, 255, 0.16); + border-color: rgba(160, 230, 255, 0.55); + color: #eaf7ff; + box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.12), 0 0 14px rgba(110, 210, 255, 0.28); } /* Контекстное меню узла (долгое нажатие) — в #modal-root, поверх всего, не масштабируется */