微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

当形状可能非常多时,如何有效地使用事件侦听器

如何解决当形状可能非常多时,如何有效地使用事件侦听器

我的一个副项目具有以下特征:

  • 舞台上的形状很多
  • 可以拖动每个形状
  • 当拖动的形状与另一个碰撞时,我需要做些事情。

我担心在所有形状上都具有拖动开始,移动和结束侦听器的开销。如何避免数千个形状上的多个侦听器?

我有一种方法,我将其发布为我自己的答案,但是如果可以更有效地实现,则请您自己回答。

解决方法

一个可能的答案是仅在放置形状的图层上使用侦听器。我实际上可以有成千上万的形状,只需要3个侦听器。

该片段是一款游戏,玩家必须将每个圆圈拖到另一个圆圈上-当拖放的圆圈掉落时,它会被删除。仅剩一个圆圈时,游戏结束。

这里的要点是,用于拖放,移动和放下的事件侦听器位于“层”上,而不是位于每个圆的“形状”上。 这意味着无论形状多少,我只有3个听众。

这里特别有趣的是使用Konva的Util.haveIntersection()函数进行所有数学运算,以检查一种形状的clientRect是否与另一种形状重叠,以及使用

layer.find('.connectable').each(function(shape) {...})

迭代形状的特定子集。

忽略重置功能,它全部包含在JS的约100条等距行中。

let cClone = null,// will be a clone of the dragged circle.
  //  inDragOp = false,// tells us that we are dragging.
  draggingShape = null,// points to the shape being dragged
  droppingShape = null,// add a stage
  stage = new Konva.Stage({
    container: 'container',width: $('#container').width(),height: $('#container').height()
  }),// make a layer to draw on
  layer = new Konva.Layer(),// make a connector line
  line = new Konva.Line({
    stroke: 'lime',strokeWidth: 4,points: []
  });

stage.add(layer);


// add shapes to the canvas
layer.add(line);

// first draw of shapes so far.
stage.draw();

// when the user starts to drag shape...
layer.on('dragstart',function(evt) {
  if (evt.target.hasName('connectable')) {

    draggingShape = evt.target;
    draggingShape.moveToTop();
    draggingShape.fill('cyan');

    cClone = draggingShape.clone(); // make a clone of the shape to remain as its ghost position indicator
    cClone.draggable(false); // clone not draggable   
    cClone.opacity(0.3); // make it ghostly
    cClone.name('');
    layer.add(cClone); // add to layer and draw.
    layer.batchDraw();

  }
});

// On each step of the movement
layer.on('dragmove',function(evt) {

  if (draggingShape) {

    // for performance,check if we are still overlapping the drop shape so we can avoid the need to examine all other poterntial overlaps.
    if (droppingShape && Konva.Util.haveIntersection(draggingShape.getClientRect(),droppingShape.getClientRect())) {
      line.points([cClone.x(),cClone.y(),droppingShape.x(),droppingShape.y()]);
      line.stroke('lime');
      layer.batchDraw(); // ... and redraw the layer.
      return false;
    }


    // Use Konvajs built-in rect overlap function to detect if the circles collide. Note this 
    // is not shape-based - it relies on comparing clientrect only!
    let cnt = 0;
    droppingShape = null;
    layer.find('.connectable').each(function(shape) {

      const overlapping = draggingShape === shape ? false : Konva.Util.haveIntersection(draggingShape.getClientRect(),shape.getClientRect());
      if (overlapping) { // hey - overlapping - so draw the connector line from the ghost to c2
        line.points([cClone.x(),shape.x(),shape.y()]);
        line.stroke('lime');
        droppingShape = shape;
        droppingShape.fill('lime');
        layer.batchDraw(); // ... and redraw the layer.
        return false;
      } else {
        shape.fill('transparent');
        line.stroke('red');
        line.points([cClone.x(),draggingShape.x(),draggingShape.y()])
      }
    })

    layer.batchDraw(); // ... and redraw the layer.
  }
});

// When the user completes the drag...
layer.on('dragend',function() {

  if (draggingShape) {
    if (droppingShape) {
      draggingShape.destroy();
    } else {
      draggingShape.position(cClone.position()); // put the dragged circle back where it started.
    }
    line.points([]); // no need for the line then !
    cClone.destroy(); // remove the clone
  }

  layer.batchDraw(); // redraw the layer.
  inDragOp = false; // clear the drag operation state

})


// reset to clean state.
$('#reset').on('click',function() {

  reset();

})

function reset() {

  layer.find('.connectable').each(function(shape) {
    shape.destroy();
  })

  let gridX = 3,gridY = 3,gap = 80;
  for (var i = 0; i < gridX; i++) {
    for (var j = 0; j < gridY; j++) {
      let c1 = new Konva.Circle({
        stroke: 'magenta',radius: 20,x: 50 + (i) * gap,y: 50 + (j) * gap,name: 'connectable',draggable: true
      });
      layer.add(c1);
    }
  }

  draggingShape = null;
  droppingShape = null;
  layer.batchDraw();
}

reset();
body {
  margin: 10;
  padding: 10;
  overflow: hidden;
  background-color: #f0f0f0;
}

#container {
  border: 1px solid silver;
  width: 500px;
  height: 300px;
}
<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>Drag and drop any circle over any other. </p>
<p>
  <button id='reset'>Reset</button>
</p>
<div id="container"></div>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。