Konva JS соединить квадраты и правильное расположение линий? - PullRequest
0 голосов
/ 31 марта 2020

Итак, я создаю инструмент рисования UML с Konva JS и KonvaReact, для этого мне нужно соединить фигуры линиями. Я видел учебник на сайте по подключенным объектам https://konvajs.org/docs/sandbox/Connected_Objects.html.

Они используют функцию get_connecter_points, которая вычисляет вероятность по линии на основе радиан на окружности.

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)
        ];
      }

Я пытаюсь найти симуляционную функцию, но не могу найти хорошее решение или найти хороший пример. Как вы можете видеть на изображении, я только что вернул в функциях x и y и x и y в функции, поэтому линии будут располагаться в левом верхнем углу каждого квадрата.

enter image description here

Цель функции должна состоять в том, чтобы поместить линии на полпути к стороне квадрата и на правильной стороне квадрата. Поэтому, когда квадрат находится ниже, он должен появиться на нижней стороне.

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

1 Ответ

1 голос
/ 02 апреля 2020

Для прямоугольников математика немного сложнее, чем для кругов.

Во-первых, вам нужно вычислить угол для соединительной линии между двумя объектами:

function getCenter(node) {
  return {
    x: node.x() + node.width() / 2,
    y: node.y() + node.height() / 2
  }
}
const c1 = getCenter(object1);
const c2 = getCenter(object2;

const dx = c1.x - c2.x;
const dy = c1.y - c2.y;
const angle = Math.atan2(-dy, dx);

Второй , когда вы знаете угол, вам нужна функция, которая находит точку границы прямоугольника, которую вы можете использовать для соединения с другим объектом.

function getRectangleBorderPoint(radians, size, sideOffset = 0) {
  const width = size.width + sideOffset * 2;

  const height = size.height + sideOffset * 2;

  radians %= 2 * Math.PI;
  if (radians < 0) {
    radians += Math.PI * 2;
  }

  const phi = Math.atan(height / width);

  let x, y;
  if (
    (radians >= 2 * Math.PI - phi && radians <= 2 * Math.PI) ||
    (radians >= 0 && radians <= phi)
  ) {
    x = width / 2;
    y = Math.tan(radians) * x;
  } else if (radians >= phi && radians <= Math.PI - phi) {
    y = height / 2;
    x = y / Math.tan(radians);
  } else if (radians >= Math.PI - phi && radians <= Math.PI + phi) {
    x = -width / 2;
    y = Math.tan(radians) * x;
  } else if (radians >= Math.PI + phi && radians <= 2 * Math.PI - phi) {
    y = -height / 2;
    x = y / Math.tan(radians);
  }

  return {
    x: -Math.round(x),
    y: Math.round(y)
  };
}

Теперь вам просто нужно сгенерировать точки для формы линии :

function getPoints(r1, r2) {
  const c1 = getCenter(r1);
  const c2 = getCenter(r2);

  const dx = c1.x - c2.x;
  const dy = c1.y - c2.y;
  const angle = Math.atan2(-dy, dx);

  const startOffset = getRectangleBorderPoint(angle + Math.PI, r1.size());
  const endOffset = getRectangleBorderPoint(angle, r2.size());

  const start = {
    x: c1.x - startOffset.x,
    y: c1.y - startOffset.y
  };

  const end = {
    x: c2.x - endOffset.x,
    y: c2.y - endOffset.y
  };

  return [start.x, start.y, end.x, end.y]
}

function updateLine() {
  const points = getPoints(rect1, rect2);
  line.points(points);
}

Все это в качестве демонстрации:

function getRectangleBorderPoint(radians, size, sideOffset = 0) {
  const width = size.width + sideOffset * 2;

  const height = size.height + sideOffset * 2;

  radians %= 2 * Math.PI;
  if (radians < 0) {
    radians += Math.PI * 2;
  }

  const phi = Math.atan(height / width);

  let x, y;
  if (
    (radians >= 2 * Math.PI - phi && radians <= 2 * Math.PI) ||
    (radians >= 0 && radians <= phi)
  ) {
    x = width / 2;
    y = Math.tan(radians) * x;
  } else if (radians >= phi && radians <= Math.PI - phi) {
    y = height / 2;
    x = y / Math.tan(radians);
  } else if (radians >= Math.PI - phi && radians <= Math.PI + phi) {
    x = -width / 2;
    y = Math.tan(radians) * x;
  } else if (radians >= Math.PI + phi && radians <= 2 * Math.PI - phi) {
    y = -height / 2;
    x = y / Math.tan(radians);
  }

  return {
    x: -Math.round(x),
    y: Math.round(y)
  };
}

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

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

const rect1 = new Konva.Rect({
  x: 20,
  y: 20,
  width: 50,
  height: 50,
  fill: 'green',
  draggable: true
});
layer.add(rect1);


const rect2 = new Konva.Rect({
  x: 220,
  y: 220,
  width: 50,
  height: 50,
  fill: 'red',
  draggable: true
});
layer.add(rect2);

const line = new Konva.Line({
  stroke: 'black'
});
layer.add(line);

function getCenter(node) {
  return {
    x: node.x() + node.width() / 2,
    y: node.y() + node.height() / 2
  }
}

function getPoints(r1, r2) {
  const c1 = getCenter(r1);
  const c2 = getCenter(r2);

  const dx = c1.x - c2.x;
  const dy = c1.y - c2.y;
  const angle = Math.atan2(-dy, dx);

  const startOffset = getRectangleBorderPoint(angle + Math.PI, rect1.size());
  const endOffset = getRectangleBorderPoint(angle, rect2.size());

  const start = {
    x: c1.x - startOffset.x,
    y: c1.y - startOffset.y
  };

  const end = {
    x: c2.x - endOffset.x,
    y: c2.y - endOffset.y
  };
  
  return [start.x, start.y, end.x, end.y]
}

function updateLine() {
  const points = getPoints(rect1, rect2);
  line.points(points);
}

updateLine();
layer.on('dragmove', updateLine);

layer.draw();
  <script src="https://unpkg.com/konva@^3/konva.min.js"></script>
  <div id="container"></div>
...