0

Let's say I have 2 Rects, one big, one small, both in the same Layer. There are no Groups and no other Shapes. Small Rect is draggable, resizable and rotatable, big one is static.

I want to make sure you can't drag the small one outside of the big one, and I want any rotation or scaling of the small Rect to be respected.

I use getClientRect on both Rects to define drag bounds. Now when I know the desired x and y for my small Rect's bounding rect, how do I set it? I cannot do setAbsolutePosition({ x, y }), because the small rect may be rotated, which means that getAbsolutePosition().x !== getClientRect().x. I tried to use getAbsoluteTransform().point() to convert the coordinates, but I had no luck.

Thanks in advance.

optimistiks
  • 491
  • 1
  • 5
  • 16
  • Can you make a demo of what you are trying? – lavrton Jan 31 '19 at 18:11
  • The snippet in the answer shows what I was trying to do pretty spot on. Basically, I was trying to solve a problem that was described in this question https://stackoverflow.com/questions/48597795/konva-using-rotate-and-keeping-within-bounds, but I wanted to avoid any rotation-related computations, and just use bounding boxes to determine drag bounds – optimistiks Feb 01 '19 at 10:42

1 Answers1

1

This may get you along the road. Rotate the rect with the transformer so it hits the red rectangle, or drag it, to see the bounds checking with the client rect effect. I used the client rect in the transformer boundBoxFunc and the rect.dragBoundFunc.

Note: there is an issue in that the calculations of the overlap somehow leave the rect such that it overlaps the boundary. A bug to be solved perhaps.

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


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

var rectOuter = new Konva.Rect({
   width: 240, height: 150, x: 80, y: 80, draggable: true, stroke: 'red'
  })
layer.add(rectOuter);

var rect = new Konva.Rect({
   width: 40, height: 50, x: 140, y: 140, draggable: true, fill: 'cyan',
  dragBoundFunc: function(newBoundBox) {
    var pos = rect.getClientRect();

    if (intersectRect(pos,  rectOuter.getClientRect())){
      return {
        x: oldBoundBox.x,
        y: oldBoundBox.y      
      }      
    }
    oldBoundBox.x = newBoundBox.x; // note old box for use if we deny the drag
    oldBoundBox.y = newBoundBox.y;
    return {
      x: newBoundBox.x,
      y: newBoundBox.y      
    }
  }  
  })
layer.add(rect);
var oldBoundBox = rect.getAbsolutePosition();

var rect2 = new Konva.Rect ({
  stroke: 'magenta', listening: false,  
})
layer.add(rect2);

var text = new Konva.Text({
  x: 5,
  y: 5,
});
layer.add(text);
updateText();


 
  // make the transformer for the image
  var transformer = new Konva.Transformer({
    node: rect,
    enabledAnchors: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
      boundBoxFunc: function (oldBoundBox, newBoundBox) {
        var pos = rect.getClientRect();
        
        if (intersectRect(pos,  rectOuter.getClientRect())){
          return oldBoundBox;
        }
        
        return newBoundBox
      }    
  });
  layer.add(transformer);

    rect.on('dragmove', function () {
      updateText();
    })
    rect.on('transform', function () {
      updateText();
    });

   function updateText() {
     var pos = rect.getClientRect();
      var lines = [
        'x: ' + rect.x(),
        'y: ' + rect.y(),
        'rotation: ' + rect.rotation(),
        'width: ' + rect.width(),
        'height: ' + rect.height(),
        'scaleX: ' + rect.scaleX(),
        'scaleY: ' + rect.scaleY(),
        'client: ' + pos.x + ', ' + pos.y
      ];
     text.text(lines.join('\n'));
    
     // use rect2 to give a view on what is happening as we translate
     rect2.position({x: pos.x, y: pos.y});
     rect2.width(pos.width);
     rect2.height(pos.height);
     
      layer.batchDraw();
    }

layer.draw()
stage.draw()

// check if the rects overlap
function intersectRect(kr1, kr2) {
  var r1 = makeGeomRect(kr1, 0); // add left & right properties
  var r2 = makeGeomRect(kr2, 10);
  
  return !(r2.left <= r1.left && 
           r2.right > r1.right && 
           r2.top < r1.top &&
           r2.bottom > r1.bottom);
}
  

// make a handier rect - takes a rect with x, y, width, height and gives it left and right 
function makeGeomRect (r, padding){
  
  var out = {
    left: r.x + padding,
    right: r.x + r.width - padding,
    top: r.y + padding,
    bottom: r.y + r.height - padding    
  }
  return out;
  
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.6.0/konva.min.js"></script>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="image-editor">
  <div id="canvas-container"></div>
</div>

Run snippet full screen to see the full glory

Vanquished Wombat
  • 9,075
  • 5
  • 28
  • 67
  • So the idea is that we just use previous absolute position as our new position (effectively denying the drag) if our bounding rectangles overlap, correct? You don't do any "conversions" of the bounding rect position to the absolute shape position? Am I right that your snippet is an easier "any-angle-of-rotation" solution of this problem: https://stackoverflow.com/questions/48597795/konva-using-rotate-and-keeping-within-bounds, without involving the "trig or matrix maths to compute rotated values of the point, etc."? – optimistiks Feb 01 '19 at 10:45
  • Yes the intention of the dragboundfunc and the transformers boundboxfunc is to get a preview of where the node is going and railroad that as required, so denying the move. Regarding conversion of position to absolute position, I agree that I may have got lucky and that some effort may be needed for offset objects, scaling, etc. I was trying to visualise the result of getClientRect() on the transform (the pink rect in the demo) for my own curiosity as to whether the trig math was needed - which it appears it is not! – Vanquished Wombat Feb 01 '19 at 12:18