如何使用 Konva JS 检测图像和文本对象之间的碰撞

How to detect a collision between an Image and a Text object using Konva JS

我正在尝试构建一个简单的应用程序,用户可以在其中将一个词拖到相应的图像上。当用户将单词“放下”到图像上时,需要检测碰撞。我有一条 console.log 消息,它应该只在发生这种情况时记录。目前,即使没有碰撞,我的消息也在记录。我哪里出错了?这是我的代码:

    //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
    );
}

第一期:传递给 haveIntersection 的第二个参数是 imgLayer,它是一个 Konva.Layer 对象。由于图层与舞台大小相同,我认为这总是 return true.

第二个问题:您的 haveIntersection() 函数需要包含 x、y、宽度和高度的对象参数。传入 Konva 对象 here/like 这是不好的做法,因为它们没有这些属性。 Konva.Text 就是这种情况,它既没有宽度也没有高度属性。

对于 x 你会参考 shape.x(),宽度会是 shape.width(),等等。你有时可以通过传递 shape.getAttrs() 来逃避,它提供了一个普通的 JS 对象包含此类属性,但即使这样也可能会失败,因为某些形状(例如 Konva.Image 在 getAttrs() 的结果中没有宽度或高度属性。原来 Konva.Rect 和 Konva.Image do 有它们,同时 Konva.text() 没有,所以你不应该依赖这个。

相反,您应该明确并传入定义的对象(参见代码片段)。明确您的意图是值得的 - 库可以随着时间的推移添加或删除默认值和行为,如果它 'had' 有效,您的代码仍然可能是一个定时炸弹。

作为奖励:在您有多个对象要检查碰撞的情况下,一种有用的技术是使用 stage,find() 函数。为 drop-target 对象指定一个通用名称 attr,您可以通过 stage.find('.name_to_find') 搜索此类对象。在代码片段中,我将名称 'dropimage' 分配给 rects(代替图像对象以避免此处的图像获取问题)。然后你可以迭代找到的对象和运行碰撞检测等

最后,您在代码中使用了两层。每层都有处理开销,虽然使用多层是一种合法的技术,但您应该考虑在这种简单情况下是否需要两层。单层就可以了。

   //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>