Связи (13.06): орбы по референсу, линии по категории, постоянная вселенная
Орбы: - материал «хрусталь»: чистое лицо (виньетка 0.5→0.22), диффузный блик окна вместо «капли» (+мягкий blur sf), стекло прозрачнее (тело 0.38→0.3), полупрозрачная преломляющая кромка (blur + opacity 0.25→0.2). - размер +11.5% (node-dot 52→58px); единый ORB_R=29 как источник радиуса. - убран значок * у общих узлов (логика is-common цела). Линии: - цвет по категории на ВСЕХ рёбрах; плазма только сияющим. - общий узел наследует сияние исходного человека (не серый). - контакт линий ровно на кромке сферы орба (ORB_R), без зазора, все уровни. Навигация: - констелляция (паутина 2-3 уровней) — постоянный режим; кнопка «Вселенная» убрана; Семья/Друзья/Сияющие остаются фильтрами. Чистка осиротевшего CSS. Версия 1.2.161. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0b4374141e
commit
f19f7b0ec4
@ -1,2 +1,2 @@
|
|||||||
client.version=1.2.160
|
client.version=1.2.161
|
||||||
server.version=1.2.144
|
server.version=1.2.144
|
||||||
|
|||||||
@ -90,6 +90,11 @@ const RELATION_COLORS = {
|
|||||||
// Неоновый цвет центра — из него «вытекает» градиент каждой связи к цвету периферийного узла.
|
// Неоновый цвет центра — из него «вытекает» градиент каждой связи к цвету периферийного узла.
|
||||||
const FOCUS_NEON = 'rgba(140, 240, 255, 0.95)';
|
const FOCUS_NEON = 'rgba(140, 240, 255, 0.95)';
|
||||||
|
|
||||||
|
// Радиус видимой сферы орба (world-единицы), синхронно с CSS `.fg-node .node-dot` = 58px → радиус 29
|
||||||
|
// (сфера орба ≈ радиусу node-dot). ЕДИНЫЙ источник радиуса для контакта линий с кромкой и раскладки детей —
|
||||||
|
// меняешь размер орба → меняй здесь и в CSS вместе, линии останутся впритык.
|
||||||
|
const ORB_R = 29;
|
||||||
|
|
||||||
|
|
||||||
function easeOutCubic(t) {
|
function easeOutCubic(t) {
|
||||||
const x = 1 - t;
|
const x = 1 - t;
|
||||||
@ -437,23 +442,24 @@ export function createForceGraph({ stage, model, onCenterTap, onNodeTap, onNodeL
|
|||||||
svg.innerHTML = ''
|
svg.innerHTML = ''
|
||||||
+ '<defs>'
|
+ '<defs>'
|
||||||
+ '<clipPath id="c' + u + '"><circle cx="50" cy="50" r="38"/></clipPath>'
|
+ '<clipPath id="c' + u + '"><circle cx="50" cy="50" r="38"/></clipPath>'
|
||||||
+ '<radialGradient id="sh' + u + '" cx="50%" cy="45%" r="52%"><stop offset="62%" stop-color="#bfe3ff" stop-opacity="0"/><stop offset="90%" stop-color="#dff1ff" stop-opacity="0.55"/><stop offset="100%" stop-color="#5f93b8" stop-opacity="0.25"/></radialGradient>'
|
+ '<radialGradient id="sh' + u + '" cx="50%" cy="45%" r="52%"><stop offset="62%" stop-color="#bfe3ff" stop-opacity="0"/><stop offset="90%" stop-color="#dff1ff" stop-opacity="0.5"/><stop offset="100%" stop-color="#5f93b8" stop-opacity="0.18"/></radialGradient>'
|
||||||
+ '<radialGradient id="sd' + u + '" cx="50%" cy="60%" r="55%"><stop offset="58%" stop-color="#000000" stop-opacity="0"/><stop offset="100%" stop-color="#02060d" stop-opacity="0.4"/></radialGradient>'
|
+ '<radialGradient id="sd' + u + '" cx="50%" cy="60%" r="58%"><stop offset="72%" stop-color="#000000" stop-opacity="0"/><stop offset="100%" stop-color="#02060d" stop-opacity="0.22"/></radialGradient>'
|
||||||
+ '<linearGradient id="sp' + u + '" x1="0" y1="0" x2="1" y2="1"><stop offset="0%" stop-color="#ffffff" stop-opacity="0.85"/><stop offset="100%" stop-color="#ffffff" stop-opacity="0"/></linearGradient>'
|
+ '<linearGradient id="sp' + u + '" x1="0" y1="0" x2="1" y2="1"><stop offset="0%" stop-color="#ffffff" stop-opacity="0.5"/><stop offset="100%" stop-color="#ffffff" stop-opacity="0"/></linearGradient>'
|
||||||
+ '<filter id="so' + u + '" x="-40%" y="-40%" width="180%" height="180%"><feGaussianBlur stdDeviation="1.3"/></filter>'
|
+ '<filter id="so' + u + '" x="-40%" y="-40%" width="180%" height="180%"><feGaussianBlur stdDeviation="1.3"/></filter>'
|
||||||
|
+ '<filter id="sf' + u + '" x="-60%" y="-60%" width="220%" height="220%"><feGaussianBlur stdDeviation="3.2"/></filter>'
|
||||||
+ '<filter id="gl' + u + '" x="-70%" y="-70%" width="240%" height="240%"><feGaussianBlur stdDeviation="' + glowSpread + '"/></filter>'
|
+ '<filter id="gl' + u + '" x="-70%" y="-70%" width="240%" height="240%"><feGaussianBlur stdDeviation="' + glowSpread + '"/></filter>'
|
||||||
+ '</defs>'
|
+ '</defs>'
|
||||||
+ '<circle cx="50" cy="50" r="42" fill="#5facd4" opacity="' + glowOp + '" filter="url(#gl' + u + ')"/>'
|
+ '<circle cx="50" cy="50" r="42" fill="#5facd4" opacity="' + glowOp + '" filter="url(#gl' + u + ')"/>'
|
||||||
+ '<circle cx="50" cy="50" r="42" fill="#0a1626" opacity="0.38"/>'
|
+ '<circle cx="50" cy="50" r="42" fill="#0a1626" opacity="0.3"/>'
|
||||||
+ '<circle cx="50" cy="50" r="38" fill="#26344a"/>'
|
+ '<circle cx="50" cy="50" r="38" fill="#26344a"/>'
|
||||||
+ (init ? '<text x="50" y="58" text-anchor="middle" fill="#cfe0ff" font-family="sans-serif" font-weight="600" font-size="22">' + init + '</text>' : '')
|
+ (init ? '<text x="50" y="58" text-anchor="middle" fill="#cfe0ff" font-family="sans-serif" font-weight="600" font-size="22">' + init + '</text>' : '')
|
||||||
+ (src ? '<image href="' + src + '" x="12" y="12" width="76" height="76" clip-path="url(#c' + u + ')" preserveAspectRatio="xMidYMid slice" style="filter:' + imgFilter + '"/>' : '')
|
+ (src ? '<image href="' + src + '" x="12" y="12" width="76" height="76" clip-path="url(#c' + u + ')" preserveAspectRatio="xMidYMid slice" style="filter:' + imgFilter + '"/>' : '')
|
||||||
+ '<circle cx="50" cy="50" r="38" fill="url(#sd' + u + ')"/>'
|
+ '<circle cx="50" cy="50" r="38" fill="url(#sd' + u + ')"/>'
|
||||||
+ '<circle cx="50" cy="50" r="42" fill="url(#sh' + u + ')"/>'
|
+ '<circle cx="50" cy="50" r="42" fill="url(#sh' + u + ')"/>'
|
||||||
+ '<circle cx="50" cy="50" r="42" fill="#2f7fd0" opacity="0.07"/>'
|
+ '<circle cx="50" cy="50" r="42" fill="#2f7fd0" opacity="0.07"/>'
|
||||||
+ '<path d="M74 16 A42 42 0 0 1 90 61" fill="none" stroke="#9fd2f2" stroke-width="1.6" stroke-opacity="0.85" stroke-linecap="round" filter="url(#so' + u + ')"/>'
|
+ '<path d="M74 16 A42 42 0 0 1 90 61" fill="none" stroke="#9fd2f2" stroke-width="1.6" stroke-opacity="0.55" stroke-linecap="round" filter="url(#so' + u + ')"/>'
|
||||||
+ '<circle cx="50" cy="50" r="42" fill="none" stroke="#a9d6f0" stroke-width="0.6" stroke-opacity="0.5"/>'
|
+ '<circle cx="50" cy="50" r="42" fill="none" stroke="#a9d6f0" stroke-width="0.9" stroke-opacity="0.2" filter="url(#so' + u + ')"/>'
|
||||||
+ '<ellipse cx="37" cy="30" rx="16" ry="8" fill="url(#sp' + u + ')" transform="rotate(-28 37 30)" filter="url(#so' + u + ')"/>';
|
+ '<ellipse cx="40" cy="31" rx="24" ry="12" fill="url(#sp' + u + ')" transform="rotate(-28 40 31)" filter="url(#sf' + u + ')"/>';
|
||||||
const im = svg.querySelector('image');
|
const im = svg.querySelector('image');
|
||||||
if (im) im.addEventListener('error', () => { try { im.remove(); } catch (e) { /* останутся инициалы */ } });
|
if (im) im.addEventListener('error', () => { try { im.remove(); } catch (e) { /* останутся инициалы */ } });
|
||||||
return svg;
|
return svg;
|
||||||
@ -634,7 +640,7 @@ export function createForceGraph({ stage, model, onCenterTap, onNodeTap, onNodeL
|
|||||||
// АДАПТИВНЫЙ радиус орбиты (фикс слипания): дети не лезут на (возможно увеличенный зумом) родитель
|
// АДАПТИВНЫЙ радиус орбиты (фикс слипания): дети не лезут на (возможно увеличенный зумом) родитель
|
||||||
// и не накладываются друг на друга. Клиренс = радиус родителя + базовый отступ; место = по числу детей.
|
// и не накладываются друг на друга. Клиренс = радиус родителя + базовый отступ; место = по числу детей.
|
||||||
const baseR = tier === 2 ? DEEP_R2 : DEEP_R3;
|
const baseR = tier === 2 ? DEEP_R2 : DEEP_R3;
|
||||||
const pr = 26 * (p.scale || 1) * (p.depthScale || 1); // визуальный радиус родителя (world-единицы)
|
const pr = ORB_R * (p.scale || 1) * (p.depthScale || 1); // визуальный радиус родителя (world-единицы)
|
||||||
const cnt = childCountByParent.get(n.parentId) || 1;
|
const cnt = childCountByParent.get(n.parentId) || 1;
|
||||||
const ringR = Math.max(baseR + pr, cnt * 13); // расталкивание: дети без наложений (клиренс + место)
|
const ringR = Math.max(baseR + pr, cnt * 13); // расталкивание: дети без наложений (клиренс + место)
|
||||||
const r = ringR * e; // при e=0 — в центре родителя, при 1 — на полной орбите
|
const r = ringR * e; // при e=0 — в центре родителя, при 1 — на полной орбите
|
||||||
@ -744,7 +750,9 @@ export function createForceGraph({ stage, model, onCenterTap, onNodeTap, onNodeL
|
|||||||
const parent = (n.parentId && nodeById.get(n.parentId)) || focus;
|
const parent = (n.parentId && nodeById.get(n.parentId)) || focus;
|
||||||
const fx = centerX + camX + parent.x * Z;
|
const fx = centerX + camX + parent.x * Z;
|
||||||
const fy = centerY + camY + parent.y * Z;
|
const fy = centerY + camY + parent.y * Z;
|
||||||
const fr = parent.dotRadius * parent.scale * (parent.depthScale ?? 1) * Z + 4;
|
// радиус контакта = реальный радиус сферы орба: полный орб = ORB_R (см. renderNodes pr=ORB_R*…),
|
||||||
|
// лёгкая точка (.fg-dot) = её dotRadius. Старое dotRadius у орбов (32/16) — легаси, давало разный зазор.
|
||||||
|
const fr = (parent.dotOnly ? parent.dotRadius : ORB_R) * parent.scale * (parent.depthScale ?? 1) * Z;
|
||||||
const nx = tx(n);
|
const nx = tx(n);
|
||||||
const ny = ty(n);
|
const ny = ty(n);
|
||||||
if ((nx < -80 && fx < -80) || (nx > viewW + 80 && fx > viewW + 80)) continue;
|
if ((nx < -80 && fx < -80) || (nx > viewW + 80 && fx > viewW + 80)) continue;
|
||||||
@ -761,8 +769,8 @@ export function createForceGraph({ stage, model, onCenterTap, onNodeTap, onNodeL
|
|||||||
const len = Math.hypot(dx, dy) || 1;
|
const len = Math.hypot(dx, dy) || 1;
|
||||||
const ux = dx / len;
|
const ux = dx / len;
|
||||||
const uy = dy / len;
|
const uy = dy / len;
|
||||||
const nr = n.dotRadius * n.scale * (n.depthScale ?? 1) * Z + 4;
|
const nr = (n.dotOnly ? n.dotRadius : ORB_R) * n.scale * (n.depthScale ?? 1) * Z;
|
||||||
// концы линии — у краёв кружков
|
// концы линии — ровно на кромке сферы орба (радиус ORB_R для полных орбов), без зазора и без захода внутрь
|
||||||
const x1 = fx + ux * fr;
|
const x1 = fx + ux * fr;
|
||||||
const y1 = fy + uy * fr;
|
const y1 = fy + uy * fr;
|
||||||
const x2 = ex - ux * nr;
|
const x2 = ex - ux * nr;
|
||||||
@ -828,9 +836,9 @@ export function createForceGraph({ stage, model, onCenterTap, onNodeTap, onNodeL
|
|||||||
parts.push(`<path d="${d}" fill="none" stroke="${relationColor(n.relationType)}" stroke-width="1.0" stroke-linecap="round" opacity="${(0.42 * pe * sp).toFixed(2)}" />`);
|
parts.push(`<path d="${d}" fill="none" stroke="${relationColor(n.relationType)}" stroke-width="1.0" stroke-linecap="round" opacity="${(0.42 * pe * sp).toFixed(2)}" />`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (shine || n.track || onPath) {
|
} else if (shine) {
|
||||||
// СИЯЮЩАЯ связь — плазменный композитинг: ОДИН центральный S-путь (cubic) + ТРИ наложенных слоя
|
// СИЯЮЩАЯ связь → цвет сияющей линии (плазма). Только сияющим — несияющие (в т.ч. активный путь
|
||||||
// с ОДИНАКОВЫМ d (объём из толщины+blur, не из геометрии). Слои: широкое поле / трубка / ядро.
|
// погружения track/onPath) идут ниже в ЦВЕТ КАТЕГОРИИ. Плазма: ОДИН S-путь + ТРИ слоя на одном d.
|
||||||
const pnx = -uy;
|
const pnx = -uy;
|
||||||
const pny = ux; // перпендикуляр к хорде
|
const pny = ux; // перпендикуляр к хорде
|
||||||
const amp = Math.min(13, 5 + segLen0 * 0.05); // амплитуда S — спокойная изящная волна (∝ длине)
|
const amp = Math.min(13, 5 + segLen0 * 0.05); // амплитуда S — спокойная изящная волна (∝ длине)
|
||||||
|
|||||||
@ -46,7 +46,7 @@ function helpText() {
|
|||||||
'• Тап по центральному узлу — здесь открылся бы профиль.',
|
'• Тап по центральному узлу — здесь открылся бы профиль.',
|
||||||
'• Долгое нажатие — контекстное меню (в реальном пути «Связи»).',
|
'• Долгое нажатие — контекстное меню (в реальном пути «Связи»).',
|
||||||
'• Чипы под шапкой — фильтры слоёв: Все / Семья / Друзья / Сияющие.',
|
'• Чипы под шапкой — фильтры слоёв: Все / Семья / Друзья / Сияющие.',
|
||||||
'• Чип «Вселенная» — режим «Интерактивная паутина» (глубина 2-3 уровней, фейковые связи).',
|
'• Вселенная — постоянный режим «Интерактивная паутина» (глубина 2-3 уровней, фейковые связи).',
|
||||||
' Наведи мышь/палец на узел — его связи временно выплывают (превью). КЛИК по узлу — «умный',
|
' Наведи мышь/палец на узел — его связи временно выплывают (превью). КЛИК по узлу — «умный',
|
||||||
' наезд»: камера летит и центрирует его, он вырастает, друзья раскрываются крупно вокруг,',
|
' наезд»: камера летит и центрирует его, он вырастает, друзья раскрываются крупно вокруг,',
|
||||||
' путь назад к Ивану горит нитью, остальное затемняется. Клик по нему ещё раз — всплыть.',
|
' путь назад к Ивану горит нитью, остальное затемняется. Клик по нему ещё раз — всплыть.',
|
||||||
@ -99,12 +99,13 @@ function addDeepLevels(model) {
|
|||||||
for (let i = 0; i < k2; i += 1) {
|
for (let i = 0; i < k2; i += 1) {
|
||||||
const id2 = `${p.id}__d2_${i}`;
|
const id2 = `${p.id}__d2_${i}`;
|
||||||
const ang2 = (i / k2) * Math.PI * 2 + seed01(p.id) * 0.6;
|
const ang2 = (i / k2) * Math.PI * 2 + seed01(p.id) * 0.6;
|
||||||
// i===0 + есть общий → подставляем узнаваемого друга Ивана как ОБЩУЮ связь (золотой ободок ★)
|
// i===0 + есть общий → подставляем узнаваемого друга Ивана как ОБЩУЮ связь (золотой ободок ★).
|
||||||
|
// Сияние НАСЛЕДУЕМ от исходного человека: если он сияющий — связь к нему тоже плазма (не серая).
|
||||||
if (i === 0 && common) {
|
if (i === 0 && common) {
|
||||||
extra.push({
|
extra.push({
|
||||||
id: id2, login: id2, name: common.name || common.login,
|
id: id2, login: id2, name: common.name || common.login,
|
||||||
avatar: null, photo: common.photo || null, relationType: common.relationType || 'friend',
|
avatar: null, photo: common.photo || null, relationType: common.relationType || 'friend',
|
||||||
strength: 0.5, shining: false, tier: 2, parentId: String(p.id), deepAngle: ang2, common: true,
|
strength: 0.5, shining: Boolean(common.shining), tier: 2, parentId: String(p.id), deepAngle: ang2, common: true,
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -181,7 +182,8 @@ export function renderNetworkLab({ navigate }) {
|
|||||||
header.classList.add('network-header-overlay');
|
header.classList.add('network-header-overlay');
|
||||||
|
|
||||||
let centerLogin = START_LOGIN;
|
let centerLogin = START_LOGIN;
|
||||||
let deepMode = false;
|
// Констелляция (паутина 2-3 уровней) — ПОСТОЯННЫЙ режим по умолчанию (чип «Вселенная» убран).
|
||||||
|
let deepMode = true;
|
||||||
|
|
||||||
// Состояние активного слоя (как в network-view): фокус всегда виден.
|
// Состояние активного слоя (как в network-view): фокус всегда виден.
|
||||||
let activeFilter = 'all';
|
let activeFilter = 'all';
|
||||||
@ -259,18 +261,8 @@ export function renderNetworkLab({ navigate }) {
|
|||||||
filterChips[key] = chip;
|
filterChips[key] = chip;
|
||||||
filterBar.append(chip);
|
filterBar.append(chip);
|
||||||
});
|
});
|
||||||
// Переключатель прототипа «Вселенная» (глубина 2-3 уровней) — отдельный чип.
|
// Чип «Вселенная» убран: констелляция — постоянный режим (deepMode=true по умолчанию).
|
||||||
const deepChip = document.createElement('button');
|
// Семья/Друзья/Сияющие остаются фильтрами поверх постоянной вселенной (см. filterBar выше).
|
||||||
deepChip.type = 'button';
|
|
||||||
deepChip.className = 'fg-filter-chip fg-deep-chip';
|
|
||||||
deepChip.textContent = '🌌 Вселенная';
|
|
||||||
deepChip.addEventListener('click', () => {
|
|
||||||
deepMode = !deepMode;
|
|
||||||
deepChip.classList.toggle('is-active', deepMode);
|
|
||||||
graph.setModel(buildLabModel(centerLogin, deepMode));
|
|
||||||
if (activeFilter !== 'all') graph.setFilter(FILTERS[activeFilter].pred);
|
|
||||||
});
|
|
||||||
filterBar.append(deepChip);
|
|
||||||
stage.append(filterBar);
|
stage.append(filterBar);
|
||||||
|
|
||||||
// --- Поиск + телепорт камеры: ввёл имя → камера летит к узлу (dive в «Вселенной», иначе перецентр) ---
|
// --- Поиск + телепорт камеры: ввёл имя → камера летит к узлу (dive в «Вселенной», иначе перецентр) ---
|
||||||
@ -329,7 +321,7 @@ export function renderNetworkLab({ navigate }) {
|
|||||||
// Автопроверки: ТОЛЬКО при ?fgtest — экспонируем граф и прогоняем self-test (результат в консоль).
|
// Автопроверки: ТОЛЬКО при ?fgtest — экспонируем граф и прогоняем self-test (результат в консоль).
|
||||||
if (typeof window !== 'undefined' && String(location.search).includes('fgtest')) {
|
if (typeof window !== 'undefined' && String(location.search).includes('fgtest')) {
|
||||||
window.__fg = graph;
|
window.__fg = graph;
|
||||||
import('./selftest.js').then((m) => m.runNetworkSelfTest(graph, deepChip)).catch((e) => console.error('[fg-selftest] не загрузился', e));
|
import('./selftest.js').then((m) => m.runNetworkSelfTest(graph, null)).catch((e) => console.error('[fg-selftest] не загрузился', e));
|
||||||
}
|
}
|
||||||
|
|
||||||
screen.cleanup = () => {
|
screen.cleanup = () => {
|
||||||
|
|||||||
@ -120,9 +120,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* круглая аватарка узла (переиспользуем существующий .node-dot из renderUserAvatar) */
|
/* круглая аватарка узла (переиспользуем существующий .node-dot из renderUserAvatar) */
|
||||||
|
/* 58px → радиус 29 = ORB_R в force-graph.js (контакт линий берётся от этого радиуса). */
|
||||||
.fg-node .node-dot {
|
.fg-node .node-dot {
|
||||||
width: 52px;
|
width: 58px;
|
||||||
height: 52px;
|
height: 58px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
|
transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
|
||||||
@ -353,14 +354,6 @@
|
|||||||
.fg-dot.is-tier3 { animation: none; }
|
.fg-dot.is-tier3 { animation: none; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Чип-переключатель «Вселенная» — активное состояние наследует стиль .fg-filter-chip.is-active */
|
|
||||||
.fg-deep-chip.is-active {
|
|
||||||
background: rgba(150, 130, 255, 0.18);
|
|
||||||
border-color: rgba(190, 170, 255, 0.6);
|
|
||||||
box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.12), 0 0 14px rgba(150, 120, 255, 0.3);
|
|
||||||
color: #efeaff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* «Призрак» старой карты при Z-переходе (эффект погружения) */
|
/* «Призрак» старой карты при Z-переходе (эффект погружения) */
|
||||||
.fg-ghost-layer {
|
.fg-ghost-layer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -678,20 +671,8 @@
|
|||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* «Общая связь» (этот человек — и твой друг тоже): золотой ободок + ★ под аватаркой */
|
/* «Общая связь» (этот человек — и твой друг тоже): золотой ободок. Значок ★ убран по запросу. */
|
||||||
.fg-node.is-common .node-dot {
|
.fg-node.is-common .node-dot {
|
||||||
border-color: rgba(255, 214, 120, 0.95);
|
border-color: rgba(255, 214, 120, 0.95);
|
||||||
box-shadow: 0 0 14px rgba(255, 200, 90, 0.4);
|
box-shadow: 0 0 14px rgba(255, 200, 90, 0.4);
|
||||||
}
|
}
|
||||||
.fg-node.is-common .node-dot::after {
|
|
||||||
content: '★';
|
|
||||||
position: absolute;
|
|
||||||
bottom: -5px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
font-size: 10px;
|
|
||||||
line-height: 1;
|
|
||||||
color: #ffd678;
|
|
||||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user