3

I have been using Konva for drawing, I would like the arrow to "snap" to the other groups or shapes when the tip of the arrow intersects them and the user lets up on the mouse. If the arrow does not interset one then it should automatically delete its self.

Then when the groups or shapes are moved I would like the tips of the arrow to move with it.

I found an example of something similar but I'm not sure how I can combine them to get what I want.

I will post my current code below.

Example link

Click here

Code

var width = height = 170;

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

var layer = new Konva.Layer();
var isDrawArrow;
var Startpos;
var Endpos;

var arrow = new Konva.Arrow({
  points: [],
  pointerLength: 10,
  pointerWidth: 10,
  fill: 'black',
  stroke: 'black',
  strokeWidth: 4
});

var circle = new Konva.Circle({
  x: stage.getWidth() / 2,
  y: stage.getHeight() / 2,
  radius: 20,
  fill: 'green'
});

var circleA = new Konva.Circle({
  x: stage.getWidth() / 5,
  y: stage.getHeight() / 5,
  radius: 30,
  fill: 'red',
  draggable: true
});

circle.on('mouseover', function() {
  document.body.style.cursor = 'pointer';
  layer.draw()
});

circle.on('mouseout', function() {
  document.body.style.cursor = 'default';
  layer.draw()
});

circle.on('mousedown touchstart', function() {
  isDrawArrow = true;
  circleA.on('dragmove', adjustPoint);
  Startpos = stage.getPointerPosition();
});

stage.addEventListener('mouseup touchend', function() {
  isDrawArrow = false;
});


stage.addEventListener('mousemove touchmove', function() {
  if (!isDrawArrow) return;
  Endpos = stage.getPointerPosition()
  
  var p = [Startpos.x, Startpos.y, Endpos.x, Endpos.y];
  arrow.setPoints(p);
  layer.add(arrow);
  layer.batchDraw();
});


circle.on('mouseup', function() {
  this.setFill('green');
  layer.batchDraw();
});


function adjustPoint(e) {
  var p = [circle.getX(), circle.getY(), circleA.getX(), circleA.getY()];

  arrow.setPoints(p);
  layer.draw();
  stage.draw();

}

function haveIntersection(r1, r2) {
  return !(
    r2.x > r1.x + r1.width ||
    r2.x + r2.width < r1.x ||
    r2.y > r1.y + r1.height ||
    r2.y + r2.height < r1.y
  );
}

layer.add(circle);
layer.add(circleA);

stage.add(layer);
adjustPoint();
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.3.0/konva.js"></script>
<div id="container"></div>
Helder Sepulveda
  • 15,500
  • 4
  • 29
  • 56
laxer
  • 720
  • 11
  • 41
  • 1
    289 lines of code is a bit much, can you trim it down to a [MCVE] perhaps? – CertainPerformance Sep 16 '18 at 01:12
  • by arrow you mean the mouse cursor? – Garr Godfrey Sep 16 '18 at 01:14
  • The way it works now is I have a star with a circle in it, if you click on the circle and move the mouse it draws an arrow with the tip at the mouse cursor and the back at the circle. I'm hoping that when the drawn arrow is over a group or shape then It will "snap" to it – laxer Sep 16 '18 at 01:18
  • @CertainPerformance I have trimmed all the HTML out, and a second group that I had that doesn't need to be there. Please let me know if there is anything I can do that would help. – laxer Sep 16 '18 at 01:39
  • 1
    Your question includes a number of components for which you should really do your own research. For making the arrow track the mouse you need to read up on getting the mouse position and mouse buttons; for deciding if the arrow is over the target circle you need to look at some simple math to decide if a point is in a circle; keeping the arrow or erasing it should then be straightforward. If you get stuck on each part then ask a question in SO, but keep it focussed on one specific issue per question. – Vanquished Wombat Sep 16 '18 at 09:03
  • I reduced your code to less than 100 lines of code and put it in a snippet, this way is easier for other to reproduce – Helder Sepulveda Sep 16 '18 at 13:36
  • @HelderSepu Thank you for shorting it down. I am still learning. – laxer Sep 16 '18 at 19:00
  • You are very welcome, But do read the help: https://stackoverflow.com/help/mcve providing code following that helps a lot when someone is troubleshooting your issue – Helder Sepulveda Sep 16 '18 at 20:00

1 Answers1

3

To do the snap you needed a function to determine distance between 2 points.
Easily done with a pythagorean calculation, (if you need help with that read about it here).

  • On the mouse move when you detect that the distance between the end of the arrow and your point (on this case the center or the red cirle) is less than what you want you can "snap it" that is what you do on your function adjustPoint that was all good.

  • On the mouse up you also need to check the distance and if is too far just hide the arrow

Working Code below:

var width = height = 170;

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

var layer = new Konva.Layer();
var isDrawArrow, Startpos, Endpos;
var snapDistance = 20;

function distance(p, c) {
  var dx = p.x - c.getX();
  var dy = p.y - c.getY();
  return Math.sqrt(dx * dx + dy * dy);
}

var arrow = new Konva.Arrow({
  points: [],
  pointerLength: 10,
  pointerWidth: 10,
  fill: 'black',
  stroke: 'black',
  strokeWidth: 4
});

var circle = new Konva.Circle({
  x: stage.getWidth() - 25,
  y: stage.getHeight() - 25,
  radius: 20,
  fill: 'green'
});

var circleA = new Konva.Circle({
  x: stage.getWidth() / 5,
  y: stage.getHeight() / 5,
  radius: 25,
  fill: 'red',
  draggable: true
});

circle.on('mousedown touchstart', function() {
  isDrawArrow = true;
  circleA.on('dragmove', adjustPoint);
  Startpos = stage.getPointerPosition();
});

stage.addEventListener('mouseup touchend', function() {
  isDrawArrow = false;
  if (distance(Endpos, circleA) > snapDistance) {
    arrow.hide();
    layer.batchDraw();
  }
});

stage.addEventListener('mousemove touchmove', function() {
  if (!isDrawArrow) return;
  Endpos = stage.getPointerPosition()

  var p = [Startpos.x, Startpos.y, Endpos.x, Endpos.y];
  arrow.setPoints(p);
  arrow.show();
  layer.add(arrow);
  layer.batchDraw();

  if (distance(Endpos, circleA) <= snapDistance) {
    adjustPoint();
    isDrawArrow = false
  }
});

function adjustPoint(e) {
  var p = [circle.getX(), circle.getY(), circleA.getX(), circleA.getY()];
  arrow.setPoints(p);
  layer.draw();
  stage.draw();
}

layer.add(circle);
layer.add(circleA);
stage.add(layer);
canvas {
  border: 1px solid #eaeaea !IMPORTANT;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.3.0/konva.js"></script>
<div id="container"></div>
Helder Sepulveda
  • 15,500
  • 4
  • 29
  • 56