Разработка визуализации графов связей (Network Graph) на сайте

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Разработка визуализации графов связей (Network Graph) на сайте
Сложная
~5 рабочих дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Разработка визуализации графов связей (Network Graph) на сайте

Network Graph — интерактивная визуализация узлов и связей: социальные графы, зависимости пакетов, схемы микросервисов, карты знаний. Требует алгоритмов force-directed layout для органичного расположения узлов.

Библиотеки

  • D3 Force — низкоуровневый, полный контроль
  • react-force-graph — React-обёртка над D3 + WebGL (2D и 3D)
  • Sigma.js — для больших графов (>1k узлов) через WebGL
  • Cytoscape.js — богатый функционал, биологические графы

react-force-graph — рекомендованный подход

npm install react-force-graph-2d
import ForceGraph2D from 'react-force-graph-2d';
import { useCallback, useRef, useState } from 'react';

interface Node {
  id: string;
  name: string;
  type: 'service' | 'database' | 'external';
  group: number;
}

interface Link {
  source: string;
  target: string;
  label?: string;
  weight?: number;
}

function ServiceDependencyGraph({ nodes, links }) {
  const graphRef = useRef();
  const [selectedNode, setSelectedNode] = useState(null);
  const [hoveredNode, setHoveredNode] = useState(null);

  // Подсветка связей выбранного узла
  const highlightedLinks = useMemo(() => {
    if (!selectedNode) return new Set();
    return new Set(
      links.filter(l => l.source === selectedNode.id || l.target === selectedNode.id)
        .map(l => `${l.source}-${l.target}`)
    );
  }, [selectedNode, links]);

  const nodeColor = useCallback((node) => {
    const colors = { service: '#3b82f6', database: '#22c55e', external: '#f59e0b' };
    if (selectedNode && node.id !== selectedNode.id) {
      const isRelated = links.some(l =>
        (l.source === selectedNode.id && l.target === node.id) ||
        (l.target === selectedNode.id && l.source === node.id)
      );
      return isRelated ? colors[node.type] : '#d1d5db';
    }
    return colors[node.type] ?? '#6b7280';
  }, [selectedNode, links]);

  const nodeCanvasObject = useCallback((node, ctx, globalScale) => {
    const label = node.name;
    const fontSize = 12 / globalScale;
    const r = 6;

    // Узел
    ctx.beginPath();
    ctx.arc(node.x, node.y, r, 0, 2 * Math.PI);
    ctx.fillStyle = nodeColor(node);
    ctx.fill();

    if (selectedNode?.id === node.id) {
      ctx.strokeStyle = '#1d4ed8';
      ctx.lineWidth = 2 / globalScale;
      ctx.stroke();
    }

    // Метка
    ctx.font = `${fontSize}px Sans-Serif`;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillStyle = '#374151';
    ctx.fillText(label, node.x, node.y + r + fontSize);
  }, [nodeColor, selectedNode]);

  const linkColor = useCallback((link) => {
    const key = `${link.source.id ?? link.source}-${link.target.id ?? link.target}`;
    return highlightedLinks.has(key) ? '#3b82f6' : '#e5e7eb';
  }, [highlightedLinks]);

  return (
    <div className="relative border border-gray-200 rounded-xl overflow-hidden" style={{ height: 600 }}>
      <ForceGraph2D
        ref={graphRef}
        graphData={{ nodes, links }}
        nodeId="id"
        nodeLabel="name"
        nodeCanvasObject={nodeCanvasObject}
        nodeCanvasObjectMode={() => 'replace'}
        linkColor={linkColor}
        linkWidth={link => (link.weight ?? 1) * 0.5}
        linkDirectionalArrowLength={3}
        linkDirectionalArrowRelPos={1}
        linkLabel="label"
        onNodeClick={(node) => {
          setSelectedNode(prev => prev?.id === node.id ? null : node);
        }}
        onNodeHover={setHoveredNode}
        onBackgroundClick={() => setSelectedNode(null)}
        d3AlphaDecay={0.02}
        d3VelocityDecay={0.3}
        warmupTicks={50}
        cooldownTicks={200}
      />

      {/* Инфопанель */}
      {selectedNode && (
        <div className="absolute top-4 right-4 bg-white border rounded-lg shadow-lg p-4 w-64">
          <h3 className="font-semibold text-gray-800">{selectedNode.name}</h3>
          <p className="text-sm text-gray-500 capitalize">{selectedNode.type}</p>
          <div className="mt-3">
            <p className="text-xs text-gray-500">Входящие связи:</p>
            <ul className="text-sm">
              {links.filter(l => (l.target.id ?? l.target) === selectedNode.id)
                .map(l => <li key={l.source.id ?? l.source}>← {l.source.name ?? l.source}</li>)}
            </ul>
          </div>
        </div>
      )}

      {/* Легенда */}
      <div className="absolute bottom-4 left-4 bg-white/90 rounded-lg p-3 text-xs">
        <div className="flex items-center gap-2 mb-1">
          <div className="w-3 h-3 rounded-full bg-blue-500" /> Сервис
        </div>
        <div className="flex items-center gap-2 mb-1">
          <div className="w-3 h-3 rounded-full bg-green-500" /> База данных
        </div>
        <div className="flex items-center gap-2">
          <div className="w-3 h-3 rounded-full bg-yellow-500" /> Внешний API
        </div>
      </div>
    </div>
  );
}

3D-граф

import ForceGraph3D from 'react-force-graph-3d';

function Graph3D({ data }) {
  return (
    <ForceGraph3D
      graphData={data}
      nodeAutoColorBy="group"
      nodeLabel="name"
      linkDirectionalParticles={2}
      linkDirectionalParticleSpeed={0.006}
      backgroundColor="#0f172a"
    />
  );
}

Большие графы с Sigma.js

npm install sigma graphology @react-sigma/core

Sigma.js рендерит через WebGL — справляется с 50k+ узлов.

import { SigmaContainer, useLoadGraph, useRegisterEvents } from '@react-sigma/core';
import Graph from 'graphology';

function LargeGraph({ graphData }) {
  return (
    <SigmaContainer style={{ height: 600 }} settings={{ nodeProgramClasses: {} }}>
      <GraphLoader data={graphData} />
    </SigmaContainer>
  );
}

Сроки

Граф с force-directed layout, подсветкой и инфопанелью — 1.5–2 недели. 3D-граф или Sigma.js для больших данных — ещё 1 неделя.