如何解决当形状可能非常多时,如何有效地使用事件侦听器
我的一个副项目具有以下特征:
我担心在所有形状上都具有拖动开始,移动和结束侦听器的开销。如何避免数千个形状上的多个侦听器?
我有一种方法,我将其发布为我自己的答案,但是如果可以更有效地实现,则请您自己回答。
解决方法
一个可能的答案是仅在放置形状的图层上使用侦听器。我实际上可以有成千上万的形状,只需要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 举报,一经查实,本站将立刻删除。