Диаграммы и схемы

Для начала работы с библиотекой Interact.js необходимо подключить её к проекту. Наиболее простой способ — использовать CDN:

<script src="https://cdn.jsdelivr.net/npm/@interactjs/interactjs/dist/interact.min.js"></script>

Альтернативно, при использовании сборщиков модулей, таких как Webpack или Vite, можно установить пакет через npm:

npm install @interactjs/interactjs

и импортировать в коде:

import interact from '@interactjs/interactjs';

Interact.js не требует дополнительных зависимостей и совместим с большинством современных браузеров.


Основные возможности для работы с элементами диаграмм

Перетаскивание (draggable)

Основная функция для интерактивных схем — это возможность перемещать элементы. Для этого используется метод .draggable().

interact('.diagram-node')
  .draggable({
    inertia: true,
    modifiers: [
      interact.modifiers.restrictRect({
        restriction: 'parent',
        endOnly: true
      })
    ],
    listeners: {
      move: dragMoveListener
    }
  });

function dragMoveListener(event) {
  const target = event.target;
  const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
  const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

  target.style.transform = `translate(${x}px, ${y}px)`;
  target.setAttribute('data-x', x);
  target.setAttribute('data-y', y);
}

Ключевые моменты:

  • inertia: true обеспечивает плавное скольжение при отпускании элемента.
  • modifiers.restrictRect ограничивает перемещение рамками родителя.
  • Атрибуты data-x и data-y используются для хранения текущего положения, что позволяет применять относительные смещения.

Масштабирование (resizable)

Для изменения размеров элементов диаграммы применяют .resizable(). Interact.js позволяет легко добавлять ручки изменения размера и настраивать ограничения.

interact('.diagram-node')
  .resizable({
    edges: { left: true, right: true, bottom: true, top: true },
    modifiers: [
      interact.modifiers.restrictSize({
        min: { width: 50, height: 50 },
        max: { width: 400, height: 400 }
      })
    ],
    listeners: {
      move(event) {
        const target = event.target;
        let x = parseFloat(target.getAttribute('data-x')) || 0;
        let y = parseFloat(target.getAttribute('data-y')) || 0;

        target.style.width = `${event.rect.width}px`;
        target.style.height = `${event.rect.height}px`;

        x += event.deltaRect.left;
        y += event.deltaRect.top;

        target.style.transform = `translate(${x}px, ${y}px)`;
        target.setAttribute('data-x', x);
        target.setAttribute('data-y', y);
      }
    }
  });

Особенности:

  • edges задаёт стороны, с которых возможна корректировка.
  • modifiers.restrictSize предотвращает выход размеров за допустимые пределы.
  • Использование deltaRect позволяет корректно обновлять позицию при изменении размеров.

Соединительные линии (gesturable и drag)

Для схем с соединениями между узлами полезно комбинировать перетаскивание узлов с динамическим рисованием линий. Interact.js поддерживает события dragstart, dragmove и dragend, которые можно использовать для обновления координат линий в реальном времени.

interact('.diagram-node')
  .draggable({
    listeners: {
      start(event) {
        event.target.classList.add('dragging');
      },
      move(event) {
        const target = event.target;
        const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
        const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

        target.style.transform = `translate(${x}px, ${y}px)`;
        target.setAttribute('data-x', x);
        target.setAttribute('data-y', y);

        updateConnections(target);
      },
      end(event) {
        event.target.classList.remove('dragging');
      }
    }
  });

function updateConnections(node) {
  const connections = document.querySelectorAll(`line[data-from='${node.id}'], line[data-to='${node.id}']`);
  connections.forEach(line => {
    const fromNode = document.getElementById(line.getAttribute('data-from'));
    const toNode = document.getElementById(line.getAttribute('data-to'));
    line.setAttribute('x1', fromNode.getBoundingClientRect().x + fromNode.offsetWidth / 2);
    line.setAttribute('y1', fromNode.getBoundingClientRect().y + fromNode.offsetHeight / 2);
    line.setAttribute('x2', toNode.getBoundingClientRect().x + toNode.offsetWidth / 2);
    line.setAttribute('y2', toNode.getBoundingClientRect().y + toNode.offsetHeight / 2);
  });
}

Важно: Использование идентификаторов узлов и атрибутов data-from/data-to позволяет динамически обновлять линии при перемещении.


События жестов (gesturable)

Для сложных интерактивных схем, например с масштабированием и вращением узлов, применяют gesturable. Это особенно актуально для touch-интерфейсов.

interact('.diagram-node')
  .gesturable({
    listeners: {
      move(event) {
        const target = event.target;

        let currentAngle = parseFloat(target.getAttribute('data-angle')) || 0;
        let currentScale = parseFloat(target.getAttribute('data-scale')) || 1;

        currentAngle += event.da;
        currentScale *= (1 + event.ds);

        target.style.transform = `translate(${target.getAttribute('data-x')}px, ${target.getAttribute('data-y')}px) rotate(${currentAngle}deg) scale(${currentScale})`;

        target.setAttribute('data-angle', currentAngle);
        target.setAttribute('data-scale', currentScale);
      }
    }
  });

Особенности:

  • event.da — изменение угла в градусах.
  • event.ds — относительное изменение масштаба.
  • Сочетание с data-x и data-y позволяет одновременно перемещать, масштабировать и вращать объект.

Объединение функций для сложных диаграмм

Interact.js позволяет комбинировать draggable, resizable и gesturable на одном элементе. При этом важно правильно управлять порядком событий и обновлением трансформаций:

interact('.diagram-node')
  .draggable({ listeners: { move: dragMoveListener } })
  .resizable({ edges: { left:true, right:true, bottom:true, top:true }, listeners: { move: resizeListener } })
  .gesturable({ listeners: { move: gestureListener } });

В этом случае необходимо централизованно отслеживать текущие значения x, y, angle и scale, чтобы трансформации не конфликтовали между собой.


Практические рекомендации

  • Хранить состояние каждого узла в атрибутах data-* или отдельной структуре данных.
  • Ограничивать область перетаскивания родительским контейнером или координатами SVG.
  • Использовать requestAnimationFrame для оптимизации отрисовки сложных соединений.
  • Разделять логику перетаскивания и изменения размеров для упрощения поддержки.

Interact.js предоставляет мощный набор инструментов для создания интерактивных диаграмм и схем с поддержкой перетаскивания, изменения размеров, вращения и масштабирования. Гибкая система событий и модификаторов позволяет полностью контролировать поведение элементов и реализовывать сложные интерфейсы без необходимости писать низкоуровневый код управления событиями.