SHA256
Связи: полировка карты связей (свечение, прорастание линий, CSS-фильтры)
- Линии: тонкие дуги Безье (градиент неон-центр → цвет роли); связь к «сияющему» монолитно светится статичной тенью drop-shadow (без бегущих импульсов). - Прорастание новых линий из центра: stroke-dasharray/dashoffset синхронно с разлётом узла (кончик трекает аватарку); старые линии исчезают мгновенно. - Ghost-слой: только аватарки (без линий), 1000мс — нет висящих «ошмётков». - CSS-bloom разлёта на компоновщике (устойчив к троттлингу rAF; завершение по таймеру). - Сияющие узлы: мягкая медленная пульсация 3.6с (многослойная box-shadow + SVG-ореол); тестовые фото-аватарки. - Фильтры слоёв в лаборатории + фикс перехвата click сценой (stopPropagation на чипах); фейд скрываемых на месте (opacity 0 + scale 0.8, 300мс), фиксация без физики (ноль тряски). - Бамп client.version → 1.2.137; обновлена документация фичи. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
f56e531384
commit
3e4759a0c9
@@ -12,6 +12,16 @@ import { openNodeMenu } from './node-menu.js';
|
||||
|
||||
const START_LOGIN = 'ivan';
|
||||
|
||||
// Фильтры слоёв — те же, что в реальном пути network-view (предикат по периферийным узлам;
|
||||
// фокус виден всегда). Позволяют пощупать в лаборатории в т.ч. слой «Сияющие».
|
||||
const FILTERS = {
|
||||
all: { label: 'Все', pred: () => true },
|
||||
family: { label: 'Семья', pred: (n) => n.relationType === 'family' },
|
||||
friends: { label: 'Друзья', pred: (n) => n.relationType === 'friend' },
|
||||
shining: { label: 'Сияющие', pred: (n) => Boolean(n.shining) },
|
||||
};
|
||||
const FILTER_ORDER = ['all', 'family', 'friends', 'shining'];
|
||||
|
||||
function helpText() {
|
||||
return [
|
||||
'Лаборатория карты связей (мок-данные, без сервера).',
|
||||
@@ -19,6 +29,7 @@ function helpText() {
|
||||
'• Тап по узлу — он стягивается в центр и карта перестраивается под его сеть.',
|
||||
'• Тап по центральному узлу — здесь открылся бы профиль.',
|
||||
'• Долгое нажатие — контекстное меню (в реальном пути «Связи»).',
|
||||
'• Чипы под шапкой — фильтры слоёв: Все / Семья / Друзья / Сияющие.',
|
||||
'',
|
||||
'Доступные люди: Иван, Алиса, Павел, Елена, Дмитрий, Олег, Нина, Марина, Света, Кирилл.',
|
||||
].join('\n');
|
||||
@@ -55,6 +66,19 @@ export function renderNetworkLab({ navigate }) {
|
||||
|
||||
const model = buildModelFromTz(graphForLogin(START_LOGIN));
|
||||
|
||||
// Состояние активного слоя (как в network-view): фокус всегда виден.
|
||||
let activeFilter = 'all';
|
||||
const filterChips = {};
|
||||
function applyFilter(key) {
|
||||
if (!FILTERS[key]) return;
|
||||
activeFilter = key;
|
||||
FILTER_ORDER.forEach((k) => {
|
||||
const el = filterChips[k];
|
||||
if (el) el.classList.toggle('is-active', k === activeFilter);
|
||||
});
|
||||
graph.setFilter(FILTERS[key].pred);
|
||||
}
|
||||
|
||||
stage.append(header);
|
||||
screen.append(stage);
|
||||
|
||||
@@ -64,7 +88,11 @@ export function renderNetworkLab({ navigate }) {
|
||||
stage,
|
||||
model,
|
||||
// тап по узлу — переключаем карту на сеть выбранного человека
|
||||
onNodeTap: (node) => { graph.setModel(buildModelFromTz(graphForLogin(node.login))); },
|
||||
onNodeTap: (node) => {
|
||||
graph.setModel(buildModelFromTz(graphForLogin(node.login)));
|
||||
// сохраняем выбранный слой при переходе на сеть другого человека (как в network-view)
|
||||
if (activeFilter !== 'all') graph.setFilter(FILTERS[activeFilter].pred);
|
||||
},
|
||||
onCenterTap: (node) => window.alert(`Профиль: ${node.name || node.login || node.id}`),
|
||||
onNodeLongPress: (node, point) => openNodeMenu({
|
||||
login: node.name || node.login || node.id,
|
||||
@@ -77,6 +105,23 @@ export function renderNetworkLab({ navigate }) {
|
||||
}),
|
||||
});
|
||||
|
||||
// Панель фильтров слоёв — тот же оверлей-стиль (.fg-filter-bar), что и в network-view.
|
||||
const filterBar = document.createElement('div');
|
||||
filterBar.className = 'fg-filter-bar';
|
||||
// Не даём нажатию на чип «провалиться» в сцену: иначе движок делает setPointerCapture на stage,
|
||||
// а захват указателя перенаправляет нативный click со сцены — и кнопка фильтра не срабатывает.
|
||||
filterBar.addEventListener('pointerdown', (e) => e.stopPropagation());
|
||||
FILTER_ORDER.forEach((key) => {
|
||||
const chip = document.createElement('button');
|
||||
chip.type = 'button';
|
||||
chip.className = `fg-filter-chip${key === activeFilter ? ' is-active' : ''}`;
|
||||
chip.textContent = FILTERS[key].label;
|
||||
chip.addEventListener('click', () => applyFilter(key));
|
||||
filterChips[key] = chip;
|
||||
filterBar.append(chip);
|
||||
});
|
||||
stage.append(filterBar);
|
||||
|
||||
screen.cleanup = () => {
|
||||
graph.destroy();
|
||||
appScreenEl?.classList.remove('network-scroll-lock');
|
||||
|
||||
Reference in New Issue
Block a user