0

Using Konvajs, I need to drag an object that is under another object WITHOUT bringing the bottom object to the top, the top object is NOT draggable.

Would appreciate any help, thank you.

OldRocker
  • 11
  • 1

1 Answers1

0

Needing to detect clicks in stacked objects is a common requirement. Fortunately Konva makes this easy. Every shape will be 'listening' for events by default. So mouse over, mouse exit, click, double click, and their touch counterparts are all available to us (see docs).

In your case you need to make the shape that is higher up the stack stop listening so that one further down can hear it.

In the example below I am using the shape.listening(value) property which controls whether the shape is listening for events (true value) or not (false value).

Looking at the snippet, if we click the green spot (where the rectangles overlap), with the upper shape set listening(false) events pass through it to the next shape in the stack and we are informed that the click was on the lower shape. With the upper shape listening(true) the upper shape receives and eats the events.

I am also using an efficient approach to event detection, which is to listen for events on the layer rather than setting listeners per shape. In the case of a few shapes on the layer, using event listeners per shape is not going to be an issue, but when you have hundreds of shapes on the layer it will affect performance.

Of course delegating the listener to the layer leaves a requirement to recognise which shape was clicked. To do this I could have used logic to match on the shape names.

// when the user starts to drag shape...
layer.on('click', function(evt) {
  if (evt.target.hasName('upperRect')) {
    alert('Click on upperRect');
  }
  if (evt.target.hasName('lowerRect')) {
    alert('Click on upperRect');
  }
  ...
})

but since all I need to do here is show the name of the clicked shape, I chose to simply use

// when the user starts to drag shape...
layer.on('click', function(evt) {
  showEvent('Click on ' + evt.target.name());
})

Finally it is worth noting that another approach to getting the clicked shape is to use the layer.getIntersection() method which requires a point parameter and will return the first shape (not a list of shapes!) that is on the layer at that point.

layer.on("click", function() { var target = layer.getIntersection(stage.getPointerPosition()); if (target) { alert('clicked on ' + target.name()); } });

Bonus note: layer.on('click') does not detect clicks on the empty part of the layer.

// Set up a stage
stage = new Konva.Stage({
    container: 'container',
    width: window.innerWidth,
    height: window.innerHeight
  }),

  // add a layer to draw on
  layer = new Konva.Layer(),

  // make the lower rect
  rectLower = new Konva.Rect({
    name: 'Lower rect',
    x: 40,
    y: 20,
    width: 100,
    height: 40,
    stroke: 'cyan',
    fill: 'cyan',
    fillEnabled: true
  }),
  
  // Make the upper rect
  rectUpper = rectLower.clone({
    name: 'Upper rect',
    stroke: 'magenta',
    fill: 'magenta',
    x: 0, 
    y: 0
  }),
  
  // add a click target for user
  spot = new Konva.Circle({
    x: 80,
    y: 30,
    fill: 'lime',
    radius: 10,
    listening: false
  });
  
  
// Add the layer to the stage and shapes to layer
stage.add(layer);
layer.add(rectLower, rectUpper, spot)
  
  
// when the user clicks a shape...
layer.on('click', function(evt) {

   showEvent('Click on ' + evt.target.name());
   
})

// Start / stop listening for events on to rect when checkbox changes.
$('#upperListening').on('change', function(){
  
  switch ($(this).is(':checked')){
    case true:
      rectUpper.listening(true);  
      showEvent('Upper rect <b>is</b> listening...');
      break;
    case false:
      rectUpper.listening(false);  
      showEvent('Upper rect <b>is not</b> listening...');
      break;
  }      
            
  // Important - if we change lsitening shapes we have to refresh hit checking list.
  layer.drawHit();
  
})

stage.draw();



function showEvent(msg){

  $('#info').html(msg)

}
body {
  margin: 10;
  padding: 10;
  overflow: hidden;
  background-color: #f0f0f0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/konva@^3/konva.min.js"></script>
<p>Click the green dot. Watch the events. Click the tickbox to stop upper rect catching events.</p>
<p><input type='checkbox' id='upperListening' checked='checked' ><label for='upperListening'>Upper is listening</label>
<p id='info'>Events show here</p>
<div id="container"></div>
Vanquished Wombat
  • 9,075
  • 5
  • 28
  • 67