Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

selecting by drawing a box around objects in konva

Tags:

konvajs

Is there an example or way of drawing a box around multiple objects on the konva canvas and by doing so selecting the objects which are inside this box with some kind of indicator around them?

like image 615
Andreas Zeiner Avatar asked Nov 29 '22 13:11

Andreas Zeiner


1 Answers

Here's a working snippet. Uses simple rect-overlap collision detection and corrects drag rect co-ordinates if user chooses to drag north or east. Should get you going.

// Set up the canvas and shapes
var s1 = new Konva.Stage({container: 'container1', width: 300, height: 200});
var layer1 = new Konva.Layer({draggable: false});
s1.add(layer1);

// draw a background rect to catch events.
var r1 = new Konva.Rect({x: 0, y: 0, width: 300, height: 200, fill: 'gold' })    
layer1.add(r1)

// draw a rectangle to be used as the rubber area
var r2 = new Konva.Rect({x: 0, y: 0, width: 0, height: 0, stroke: 'red', dash: [2,2]})    
r2.listening(false); // stop r2 catching our mouse events.
layer1.add(r2)

var shape1;
var shapeList=[];
for ( var i = 1; i <= 6; i = i + 1){
  if (i % 2 == 0){
      shape1 = new Konva.Rect({x: 100, y: 25 * i, width: 30, height: 25, fill: 'gold', stroke: 'black' })    
      shape1.on('click', function(e){
        console.log(this.x() + ', ' + this.y())
      })
  }
  if (i % 2 == 1){
      shape1 = new Konva.Circle({x: 200, y: 30 * i, radius: 15, fill: 'red', stroke: 'black'})    
  }
  shapeList.push(shape1);  // add shape to our array of shapes to hit test later
  shape1.listening(false); // stop shape interferring with mouse
  layer1.add(shape1); 
}

s1.draw() // First draw of canvas.
var posStart;
var posNow;
var mode = '';
function startDrag(posIn){
  posStart = {x: posIn.x, y: posIn.y};
  posNow = {x: posIn.x, y: posIn.y};
}

function updateDrag(posIn){ 
  
  // update rubber rect position
  posNow = {x: posIn.x, y: posIn.y};
  var posRect = reverse(posStart,posNow);
  r2.x(posRect.x1);
  r2.y(posRect.y1);
  r2.width(posRect.x2 - posRect.x1);
  r2.height(posRect.y2 - posRect.y1);
  r2.visible(true);  
 
  // run the collision check loop
  for (i=0; i< shapeList.length; i = i + 1){
    
    if (hitCheck(shapeList[i], r2)){
        shapeList[i].stroke('lime'); // if we get a hit draw a lime stroke
        }
    else {
        shapeList[i].stroke('black'); // otherwise leave stoke black.
    }
  }
  
  s1.draw(); // redraw any changes.
  
}

// start the rubber drawing on mouse down.
r1.on('mousedown', function(e){ 
  mode = 'drawing';
  startDrag({x: e.evt.layerX, y: e.evt.layerY})
  })

// update the rubber rect on mouse move - note use of 'mode' var to avoid drawing after mouse released.
r1.on('mousemove', function(e){ 
    if (mode === 'drawing'){
      updateDrag({x: e.evt.layerX, y: e.evt.layerY})
    }
})

r1.on('mouseup', function(e){ 
    mode = '';
    r2.visible(false);
    s1.draw();
})


function hitCheck(shape1, shape2){
  
  var s1 = shape1.getClientRect(); // use this to get bounding rect for shapes other than rectangles.
  var s2 = shape2.getClientRect();

  // corners of shape 1
  var X = s1.x;
  var Y  = s1.y
  var A = s1.x + s1.width;
  var B = s1.y + s1.height;

  // corners of shape 2
  var X1 = s2.x;
  var A1 = s2.x + s2.width;
  var Y1 = s2.y
  var B1 = s2.y + s2.height;

  // Simple overlapping rect collision test
  if (A<X1 || A1<X || B<Y1 || B1<Y){
      return false
  }
  else{
    return true;
  }
    
}

// reverse co-ords if user drags left / up
function reverse(r1, r2){
  var r1x = r1.x, r1y = r1.y, r2x = r2.x,  r2y = r2.y, d;
  if (r1x > r2x ){
    d = Math.abs(r1x - r2x);
    r1x = r2x; r2x = r1x + d;
  }
  if (r1y > r2y ){
    d = Math.abs(r1y - r2y);
    r1y = r2y; r2y = r1y + d;
  }
    return ({x1: r1x, y1: r1y, x2: r2x, y2: r2y}); // return the corrected rect.     
}
p
{
  padding: 13px;
  
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.5/konva.min.js"></script>
<p>Click & drag on the background to capture shapes. Drag left-top to right-bottom or in reverse. Uses simple rect-overlap collision detection.
</p>
<div id='container1' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>
like image 188
Vanquished Wombat Avatar answered Apr 01 '23 16:04

Vanquished Wombat