Skip to main content

使用 Konva 连接 HTML5 画布中的对象

如何用线或箭头连接两个对象?

Konva 不能自动连接两个对象并更新其位置。您必须手动更新线的位置。通常,当用户拖动连接的对象之一时,我们需要更新线的位置。在简单的情况下,可以像这样完成:

const obj1 = new Konva.Circle({ ...obj1Props })
const obj2= new Konva.Circle({ ...obj2Props });

const line = new Konva.Line({ ...lineProps });

obj1.on('dragmove', updateLine);
obj2.on('dragmove', updateLine);

function updateLine() {
line.points([obj1.x(), obj1.y(), obj2.x(), obj2.y]);
}

但是在这个演示中,我们将创建一个更复杂的案例,涉及应用程序的状态和多个连接的对象。

说明:尝试拖动圆圈,查看连接如何更新。

import Konva from 'konva';

const width = window.innerWidth;
const height = window.innerHeight;

const stage = new Konva.Stage({
  container: 'container',
  width: width,
  height: height,
});

const layer = new Konva.Layer();
stage.add(layer);

// 生成“目标”(圆圈)列表的函数
function generateTargets() {
  const number = 10;
  const result = [];
  while (result.length < number) {
    result.push({
      id: 'target-' + result.length,
      x: stage.width() * Math.random(),
      y: stage.height() * Math.random(),
    });
  }
  return result;
}

const targets = generateTargets();

// 生成目标之间的箭头的函数
function generateConnectors() {
  const number = 10;
  const result = [];
  while (result.length < number) {
    const from = 'target-' + Math.floor(Math.random() * targets.length);
    const to = 'target-' + Math.floor(Math.random() * targets.length);
    if (from === to) {
      continue;
    }
    result.push({
      id: 'connector-' + result.length,
      from: from,
      to: to,
    });
  }
  return result;
}

function getConnectorPoints(from, to) {
  const dx = to.x - from.x;
  const dy = to.y - from.y;
  let angle = Math.atan2(-dy, dx);

  const radius = 50;

  return [
    from.x + -radius * Math.cos(angle + Math.PI),
    from.y + radius * Math.sin(angle + Math.PI),
    to.x + -radius * Math.cos(angle),
    to.y + radius * Math.sin(angle),
  ];
}

const connectors = generateConnectors();

// 从应用程序的状态更新画布上的所有对象
function updateObjects() {
  targets.forEach((target) => {
    const node = layer.findOne('#' + target.id);
    node.x(target.x);
    node.y(target.y);
  });
  connectors.forEach((connect) => {
    const line = layer.findOne('#' + connect.id);
    const fromNode = layer.findOne('#' + connect.from);
    const toNode = layer.findOne('#' + connect.to);

    const points = getConnectorPoints(
      fromNode.position(),
      toNode.position()
    );
    line.points(points);
  });
}

// 为应用程序生成节点
connectors.forEach((connect) => {
  const line = new Konva.Arrow({
    stroke: 'black',
    id: connect.id,
    fill: 'black',
  });
  layer.add(line);
});

targets.forEach((target) => {
  const node = new Konva.Circle({
    id: target.id,
    fill: Konva.Util.getRandomColor(),
    radius: 20 + Math.random() * 20,
    shadowBlur: 10,
    draggable: true,
  });
  layer.add(node);

  node.on('dragmove', () => {
    // 修改状态
    target.x = node.x();
    target.y = node.y();

    // 根据新状态更新节点
    updateObjects();
  });
});

updateObjects();