Search code examples
javascripthtmlcanvaskonvajsgojs

Auto-adjustable line between canvas objects


My intention is to create a line with canvas that adjusts itself whenever the objects change position, so that it always has a correct shape.

I currently have the following (uses a quadratic curve to make the turn):

actual line

I have found a GoJS library that does exactly what I want, which is the following:

gojs

The problem is that the library is not open source, so I wanted to know if this is some kind of generic algorithm to do it, or I have to do the calculation manually to identify the positions of the objects and calculate the best trajectory.

As extra information, I am making use of KonvaJS which natively does not implement anything like this.


Solution

  • It depends on how much complexity you need. This is a fiddle that does the basic pathfinding you have in the animation (without rounding the corners).

    It figures out what side the paths should start from, and turns that into the offset for the path start and end, by comparing the x and y distances of the two objects:

    let startsVertical = Math.abs(dx) < Math.abs(dy);
    if (startsVertical) {
        anchorPointOffset = {x: 0, y: Math.sign(dy) * circleRadius};
    } else {
        anchorPointOffset = {x: Math.sign(dx) * circleRadius, y: 0};
    }
    let stationaryAnchorPoint = {
      x: stationaryPosition.x + anchorPointOffset.x, 
      y: stationaryPosition.y + anchorPointOffset.y
    };
    let movingAnchorPoint = {
      x: movingPosition.x - anchorPointOffset.x, 
      y: movingPosition.y - anchorPointOffset.y
    };
    

    If your shapes do not have the same width and height, you will need to use two variables, instead of circleRadius.

    Then it calculates the center point and uses that to create the middle two points of the path.

    if (startsVertical) {
      middleA = {
        x: stationaryAnchorPoint.x,
        y: centerPoint.y
      }
      middleB = {
        x: movingAnchorPoint.x,
        y: centerPoint.y
      }
    } else {
      middleA = {
        x: centerPoint.x,
        y: stationaryAnchorPoint.y
      }
      middleB = {
        x: centerPoint.x,
        y: movingAnchorPoint.y
      }
    }
    

    Rounding the corners is a little more complicated, but there are many guides for that.