1

I am trying to build a simple app in which the user drags a word onto the corresponding image. When the user "drops" the word on the image a collision needs to be detected. I have a console.log message which should only log when this happens. Currently my message is logging even when there is no collision. Any ideas where I'm going wrong? Here is my code:

    //create stage to hold the text and image canvases
var stage = new Konva.Stage({
    container: 'container',
    width: window.innerWidth,
    height: window.innerHeight,
  });


//***************************************  IMG CANVAS *************************************/
// // add img canvas element
var imgLayer = new Konva.Layer();
stage.add(imgLayer);



    var imageObj = new Image();
imageObj.onload = function () {
  var item = new Konva.Image({
    x: 200,
    y: 200,
    image: imageObj,
    width: 400,
    height: 400,
    stroke: 'black',
  });

  // add the shape to the layer
  imgLayer.add(item);
};
imageObj.src = 'assets/apple.jpg';

//***************************************  TEXT CANVAS *************************************/
// add text canvas element
var textLayer = new Konva.Layer();
stage.add(textLayer);

// create text
var text = new Konva.Text({
  x: 350,
  y: 0,
  text: 'apple',
  fontSize: 40,
  fontFamily: 'Calibri',
  fill: 'blue',
  draggable: true,
});
textLayer.add(text);

// add text cursor styling
text.on('mouseover', function () {
  document.body.style.cursor = 'pointer';
});
text.on('mouseout', function () {
  document.body.style.cursor = 'default';
});



//************************************* COLLISION ***************************************/

text.on('dragend', (e)=>{
    const target = e.target;
    console.log(target);
    const targetImg = imgLayer;
    console.log(targetImg);

    if (haveIntersection(target, imgLayer)) {
        console.log("collision");
        
    } else {
        console.log('no collision');
    }
});

const haveIntersection= (r1, r2) => {
    return !(
        r2.x > r1.x + r1.width ||
        r2.x + r2.width < r1.x ||
        r2.y > r1.y + r1.height ||
        r2.y + r2.height < r1.y
    );
}
KathA
  • 33
  • 5

1 Answers1

1

First issue: The second parameter you pass to haveIntersection is imgLayer, which is a Konva.Layer obect. Since a layer is the same size as the stage I assume this is always going to return true.

Second issue: Your haveIntersection() function expects object parameters with x, y, width & height. Passing in Konva objects here/like this is bad practice because they do not have those attributes. This is the case with Konva.Text which has simple neither width not height attrs.

For x you would refer to shape.x(), width would be shape.width(), etc. You can sometimes get away with passing shape.getAttrs() which provides a plain JS object containing such attrs, but even that is likely to fail as some shapes such as Konva.Image do not have the width or height attrs in the result of getAttrs(). It turns out that Konva.Rect and Konva.Image do have them, meanwhile Konva.text() does not, so you should not rely on this.

Instead, you should be explicit and pass in a defined object (see snippet code). It pays to be clear about what you intend - libs can add or remove defaults and behaviours over time and if it 'had' worked your code would still have been a possible time bomb.

As a bonus: A useful technique for case where you have multiple objects to check for collision is to use the stage,find() function. Give the drop-target objects a common name attr and you can search for such objects via stage.find('.name_to_find'). In the snippet I assign the name 'dropimage' to the rects (in replacement for image object so as to avoid image fetching issues here). Then you can iterate the found objects and run the collision detection, etc.

Lastly, you are using two layers in the code. Each layer has a processing overhead and whilst using multiple layers is a legitimate technique, you should consider whether you need two layers in this simple case. A single layer would do fine.

   //create stage to hold the text and image canvases
var stage = new Konva.Stage({
    container: 'container',
    width: window.innerWidth,
    height: window.innerHeight,
  });


//***************************************  IMG CANVAS *************************************/
// add img layer
var imgLayer = new Konva.Layer();
stage.add(imgLayer);

let r = new Konva.Rect({
  x: 40,
  y: 20,
  width: 200,
  height: 150,
  fill: 'cyan',
  name: 'dropimage'    
  })
imgLayer.add(r);

r = new Konva.Rect({
  x: 340,
  y: 60,
  width: 200,
  height: 150,
  fill: 'magenta',
  name: 'dropimage'    
  })
imgLayer.add(r);


//***************************************  TEXT CANVAS *************************************/
// add text canvas element
var textLayer = new Konva.Layer();
stage.add(textLayer);

// create text
var text = new Konva.Text({
  x: 350,
  y: 0,
  text: 'apple',
  fontSize: 40,
  fontFamily: 'Calibri',
  fill: 'blue',
  draggable: true,
});
textLayer.add(text);

// add text cursor styling
text.on('mouseover', function () {
  document.body.style.cursor = 'pointer';
});
text.on('mouseout', function () {
  document.body.style.cursor = 'default';
});



//************************************* COLLISION ***************************************/

text.on('dragend', (e)=>{
    const target = e.target;

    let images = stage.find('.dropimage');

    let cnt = 0;
    for (let img of images){
      cnt++;

      if (haveIntersection({
            x: target.x(), 
            y: target.y(), 
            width: target.width(), 
            height: target.height()
            }, 
            img.getAttrs()
          )) {
          console.log("collision on shape " + cnt);
      } else {
          console.log("no collision on shape " + cnt);
      }
    
    }
    

});

const haveIntersection= (r1, r2) => {
    return !(
        r2.x > r1.x + r1.width ||
        r2.x + r2.width < r1.x ||
        r2.y > r1.y + r1.height ||
        r2.y + r2.height < r1.y
    );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/8.3.0/konva.min.js"></script>
<p>Drag text onto a rect and look in console</p>
<div id='container'></div>
Vanquished Wombat
  • 9,075
  • 5
  • 28
  • 67
  • Thank you Vanquished Wombat!!! This was the first time I had ever used Konva as well as the first time I've ever tried to code using a canvas element, so thank you :). – KathA Jan 25 '22 at 11:35
  • On closer look, can you explain to me how the target.x() and target.y() functions work when setting the x and y keys of the first object passed to the haveIntersection() function? I don't understand where the x() or y() functions are defined - are they built in methods? Hope this makes sense. Thank you – KathA Jan 25 '22 at 12:45
  • e.target is gives a Konva object - in this case its the text object we dragged. Most attrs of Konva objects are accessed by functions, so instead of target.x we use target.x() to get the x position of the object, and target.x(some number) to set the x position. You 'can' use the let attrObj = target.getAttrs() to return a plain JS obejct containing many of the attributes but it is not guaranteed that all shapes have all attributes there, not even width and height in the case of the Konva.Text. The [Konva API docs](https://konvajs.org/api/Konva.html) is a good resource to get familiar with. – Vanquished Wombat Jan 26 '22 at 09:18
  • Thank you so much. I'm very familiar with javascript so understand the e.target but it was the fact that Konva objects are accessed by functions that I wasn't aware of. I have found the Konva docs quite difficult to understand - I don't find them as user-friendly as most of the other docs I've read. Anyway - thanks again :)) – KathA Jan 27 '22 at 10:21
  • Sorry not intending to patronise. Re functions vs properties, AFIK Konva pre-dates object construction with simple properties (getters/setters). I guess there were ways around that back then but the focus would have been on performance so hence the use of functions where getters/setters would now be the norm. IMHO this is a minor bump in the road that you will quickly absorb and will become a wrinkle. There's a friendly Discord channel which is a good resource and where you can get pointers etc. – Vanquished Wombat Jan 27 '22 at 11:51
  • BTW if I gave the correct answer can you give it the green tick. This is useful for future readers / researchers and I get the rep points. Thanks – Vanquished Wombat Jan 27 '22 at 11:54
  • 1
    Thanks Vanquished Wombat :)). I've ticked the tick!! First time I posted here so still getting the hang of it. Thanks for all your help :-)) – KathA Jan 28 '22 at 12:14