Библиотека GoJS: создание интерактивных диаграмм

Раздел: Библиотеки -> Библиотеки для визуализации

Работа с библиотекой GoJS для построения диаграмм

GoJS - это мощная JavaScript-библиотека для создания интерактивных диаграмм, графов и схем. Она поддерживает широкий набор визуальных элементов, автоматические раскладки, привязку данных и пользовательские взаимодействия. Библиотека распространяется по коммерческой лицензии, но доступна для ознакомления в режиме trial.

Как создать простую блок-схему с помощью GoJS?

Рассмотрим базовый сценарий - построение диаграммы с тремя узлами и связями между ними. Для этого подключим GoJS через CDN, создадим экземпляр Diagram и определим шаблоны узлов и связей.

<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/gojs@2.3.22/release/go.js"></script>
</head>
<body>
  <div id="myDiagramDiv" style="width:800px; height:600px; border:1px solid black"></div>
  <script>
    const $ = go.GraphObject.make;
    const diagram = $(go.Diagram, "myDiagramDiv");

    // шаблон узла
    diagram.nodeTemplate =
      $(go.Node, "Auto",
        $(go.Shape, "RoundedRectangle", { fill: "lightblue", stroke: "blue" }),
        $(go.TextBlock, { margin: 10, text: "Узел" },
          new go.Binding("text", "key"))
      );

    // шаблон связи
    diagram.linkTemplate =
      $(go.Link,
        $(go.Shape, { stroke: "gray", strokeWidth: 2 }),
        $(go.Shape, { toArrow: "Standard", fill: "gray" })
      );

    // данные модели
    diagram.model = new go.GraphLinksModel(
      [
        { key: "Начало" },
        { key: "Процесс" },
        { key: "Конец" }
      ],
      [
        { from: "Начало", to: "Процесс" },
        { from: "Процесс", to: "Конец" }
      ]
    );
  </script>
</body>
</html>

Go js (библиотека gojs для диаграмм)

После загрузки страницы отобразится диаграмма с тремя узлами и стрелками. Узлы имеют закруглённые углы, синюю обводку и светло-голубой фон. Связи рисуются серыми линиями со стрелками на конце.

Возможные трудности: если используете локальный файл, убедитесь, что скрипт загружается из CDN или скачана полная версия GoJS. При работе без интернета GoJS не загрузится. Также проверьте, что идентификатор div совпадает с аргументом в go.Diagram. Типичная ошибка - забыть указать размеры контейнера.

Как использовать GoJS внутри React-приложения?

Для интеграции с React требуется обёртка, которая управляет жизненным циклом Diagram. Один из подходов - создать компонент, который инициализирует диаграмму в useEffect и уничтожает её при размонтировании.

import React, { useEffect, useRef } from 'react';
import go from 'gojs';

const GoDiagram = () => {
  const divRef = useRef(null);

  useEffect(() => {
    const $ = go.GraphObject.make;
    const diagram = $(go.Diagram, divRef.current);

    diagram.nodeTemplate =
      $(go.Node, "Auto",
        $(go.Shape, "RoundedRectangle", { fill: "white" }),
        $(go.TextBlock, { margin: 10 },
          new go.Binding("text", "key"))
      );

    diagram.model = new go.GraphLinksModel(
      [{ key: "A" }, { key: "B" }],
      [{ from: "A", to: "B" }]
    );

    return () => diagram.div = null;
  }, []);

  return <div ref={divRef} style={{ width: '100%', height: '500px' }} />;
};

export default GoDiagram;

Chart js script (библиотека chart.js)

Компонент использует ref вместо ID, что удобно для повторного использования. Очистка при размонтировании предотвращает утечки памяти.

Возможные проблемы: если React рендерит компонент несколько раз (например, при строгом режиме), useEffect может сработать дважды, создав две диаграммы. В таких случаях нужно добавить флаг или использовать useRef для хранения экземпляра diagram.

Как изменить внешний вид узлов с использованием пользовательских SVG-шаблонов?

GoJS позволяет задавать сложные фигуры и встраивать интерактивные элементы. Например, создадим узел с иконкой и текстом, используя панели.

diagram.nodeTemplate =
  $(go.Node, "Horizontal",
    $(go.Picture, { source: "https://example.com/icon.png", width: 30, height: 30 }),
    $(go.TextBlock, { margin: 5, font: "14px sans-serif" },
      new go.Binding("text", "name"))
  );

Для более сложных форм (например, «шестиугольник») можно использовать Geometry:

$(go.Shape,
  { geometryString: "M0,0 L40,0 L50,30 L40,60 L0,60 L-10,30 Z", fill: "lightgreen" })

Такие шаблоны позволяют строить диаграммы, не ограниченные стандартными фигурами.

Ошибки: при указании geometryString важен порядок точек и замыкание контура. Неверная строка приведёт к искажённой фигуре или пустому узлу.

Как реализовать автоматическую раскладку (layout) дерева?

Для иерархических графов используйте TreeLayout. Достаточно заменить свойство layout диаграммы.

diagram.layout = $(go.TreeLayout, { angle: 90, layerSpacing: 40, nodeSpacing: 20 });
// ... далее модель должна содержать данные с указанием parent

Пример модели для дерева:

diagram.model = new go.TreeModel([
  { key: "Корень" },
  { key: "Дочерний1", parent: "Корень" },
  { key: "Дочерний2", parent: "Корень" }
]);

Диаграмма автоматически расположится сверху вниз. В GoJS доступны также ForceDirectedLayout, CircularLayout и LayeredDigraphLayout.

Важно: модель должна соответствовать типу раскладки. TreeModel - для дерева, GraphLinksModel - для произвольного графа. Несоответствие типов приведёт к тому, что узлы не будут соединены.

Как добавить поддержку перетаскивания (drag & drop) узлов?

По умолчанию узлы в GoJS можно перетаскивать, если не отключено свойство movable. Для добавления возможности перетаскивания из внешнего источника (например, панели инструментов) требуется обработчик события.

// Разрешить перетаскивание из HTML-элемента
diagram.div.addEventListener('dragover', e => {
  e.preventDefault();
});
diagram.div.addEventListener('drop', e => {
  const data = e.dataTransfer.getData('text');
  const point = diagram.lastInput.documentPoint;
  diagram.model.addNodeData({ key: data });
  // новое положение узла
  const node = diagram.findNodeForKey(data);
  if (node) node.location = point;
});

Также можно настроить MouseDrop событие самого Diagram.

Частая ошибка - не вызывать preventDefault в dragover, иначе событие drop не сработает. Кроме того, координаты точки нужно преобразовывать из window-координат в document-координаты диаграммы.

Расширенные примеры работы с GoJS

Пример 1: Синхронизация данных диаграммы с внешним JSON

Часто данные диаграммы приходят с сервера. Ниже показано, как загрузить модель из JSON и обновить её в реальном времени.

Пример
async function loadDiagram() {
  const response = await fetch('/api/diagram');
  const data = await response.json();
  diagram.model = new go.GraphLinksModel(data.nodes, data.links);
}

// Обновление при каждом изменении: слушаем событие 'Modified'
diagram.addModelChangedListener(e => {
  if (e.isTransactionFinished) {
    const modelData = diagram.model.toJSON();
    sendToServer(modelData);  // отправка на бэкенд
  }
});

Это позволяет создавать многопользовательские редакторы. Важно сериализовать модель в формат, совместимый с GoJS. Метод toJSON возвращает объект, который можно сохранить.

Пример 2: Привязка данных по загрузке изображения в узел

Узлы могут отображать изображения, загружаемые асинхронно. Используем Binding с конвертером.

Пример
diagram.nodeTemplate =
  $(go.Node, "Auto",
    $(go.Shape, { fill: "white" }),
    $(go.Panel, "Vertical",
      $(go.Picture, { width: 80, height: 80, imageStretch: go.GraphObject.UniformToFill },
        new go.Binding("source", "imageUrl").makeTwoWay(),
        new go.Binding("visible", "imageUrl", url => url ? true : false)
      ),
      $(go.TextBlock, { margin: 5 },
        new go.Binding("text", "label"))
    )
  );

// данные
{ key: 1, label: "Пример", imageUrl: "https://example.com/photo.jpg" }

Если URL недоступен, изображение скроется, и узел покажет только текст.

Пример 3: Создание пользовательского инструмента для рисования связей

По умолчанию связи рисуются через инструмент LinkingTool. Можно расширить его, чтобы добавлять логику при создании связи.

Пример
class CustomLinkingTool extends go.LinkingTool {
  doActivate() {
    // дополнительная проверка перед началом рисования
    if (!this.diagram.selection.first() || !this.diagram.selection.first() instanceof go.Node) {
      alert("Выберите узел-источник");
      return;
    }
    super.doActivate();
  }
}

diagram.toolManager.linkingTool = new CustomLinkingTool();

Такой подход полезен, когда нужно ограничить типы связей или добавить подтверждение.

Пример 4: Экспорт диаграммы в PNG с высоким разрешением

GoJS поддерживает встроенный экспорт в изображение. Можно задать масштаб.

Пример
function exportToPNG() {
  const imgData = diagram.makeImageData({
    scale: 2,        // Retina-качество
    background: "white",
    type: "image/png"
  });
  const link = document.createElement('a');
  link.download = 'diagram.png';
  link.href = imgData;
  link.click();
}

Результат - изображение с разрешением в два раза выше экранного.

Пример 5: Интерактивная панель свойств при клике на узел

При выборе узла можно отображать его свойства в отдельном блоке. Используем событие DiagramEvent.

Пример
diagram.addDiagramListener('ChangedSelection', e => {
  const selection = diagram.selection.first();
  if (selection instanceof go.Node) {
    const data = selection.data;
    document.getElementById('propertyPanel').innerHTML =
      `Имя: ${data.key}
Описание: ${data.description || ''}`; } });

Это позволяет строить редакторы диаграмм с контекстной панелью.

Библиотека GoJS для диаграмм - comments

En
Go js (javascript)