Search code examples
konvajskonva

How to drag a line by its ends / line on a group


Posting this self-answer question for my own future reference and in case it helps anyone else passing this way.

I have a straight line with circles at each end to act as anchors for positioning the line. These shapes are in a group. I want the user to be able to click and drag a circle and have the line follow the movement of the circle.

Simple line with end anchors

The group is draggable and when I drag either circle the entire group is moved - what I want is to drag the circles independently of the group.

const stage = new Konva.Stage({
  container: 'container',
  width: window.innerWidth,
  height: window.innerHeight,
}),
layer = new Konva.Layer({name: 'layer'}),
group = new Konva.Group({name: 'group', draggable: true}),
line = new Konva.Line({
  name: 'the line',
  x: 20,
  y: 20,
  points: [0, 0, 500, 200],
  strokeWidth: 5,
  stroke: 'cyan'
}),
circle1 = new Konva.Circle({
  name: 'circle1',
  x: 20,
  y: 20,
  radius: 10,
  fill: 'magenta'
}),
circle2 = circle1.clone({name: 'circle2', x: 520, y: 220, fill: 'magenta'});

group.add(line, circle1, circle2)
stage.add(layer)
layer.add(group);

// What next ?

 
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/konva@8.3.2/konva.min.js"></script>
    <div id="container"></div>


Solution

  • Found the answer: Make the circles draggable, then use the position of the dragged circle to give the position to move the appropriate end of the line to.

    When shapes are on a group, their position is relative to the group parent. I wondered if this would be a complication. However since the line and circles are all children of the same group parent, we can get the plain shape.position() call to find the position we need. shape.position() gets the position relative to the parent. For future reference, you would use getAbsolutePosition() to find the position in relation to the stage but you don't need that for this case because all shapes are on the same parent and therefore everything is relative to that parent.

    const stage = new Konva.Stage({
      container: 'container',
      width: window.innerWidth,
      height: window.innerHeight,
    }),
    layer = new Konva.Layer({name: 'layer'}),
    group = new Konva.Group({name: 'group', draggable: true}),
    line = new Konva.Line({
      name: 'the line',
      x: 20,
      y: 20,
      points: [0, 0, 500, 200],
      strokeWidth: 5,
      stroke: 'cyan'
    }),
    circle1 = new Konva.Circle({
      name: 'circle1',
      x: 20,
      y: 20,
      radius: 10,
      fill: 'magenta',
      draggable: true
    }),
    circle2 = circle1.clone({name: 'circle2', x: 520, y: 220, fill: 'magenta'});
    
    group.add(line, circle1, circle2)
    stage.add(layer)
    layer.add(group);
    
    
    circle1.on('dragmove', function(event){
      const circle1Pos = circle1.getPosition(),
            circle2Pos = circle2.getPosition();  
      line.position(circle1Pos);
      line.points([0, 0, circle2Pos.x - circle1Pos.x, circle2Pos.y - circle1Pos.y])
    })
    
    circle2.on('dragmove', function(event){
      const linePos = line.getPosition(),
            circle2Pos = circle2.getPosition();  
      line.points([0, 0, circle2Pos.x - linePos.x, circle2Pos.y - linePos.y])
    })
      
    <script src="https://unpkg.com/konva@8.3.2/konva.min.js"></script>
    <p>Click & drag circles to reposition the line. Drag line to move group.</p>
        <div id="container"></div>