Only update the z-indices that do change after reordering nodes

Right now, when updating the z-index of a node in a container,
the indices of all its siblings are refreshed. However, if the
node changes its z-index from z1 to z2, we only need to refresh
the z-indexes of the node's parent's children in the closed
range [ min(z1,z2), max(z1,z2) ]. For groups and layers with
many children that will be more optimal in many cases.

In addition to the above change, this patch:
* forces the parameter of Node#setZIndex() to be in the valid
  range.
* adds the (now absent) unit tests for the methods of Node that
  change z-indices of the parent's children.
This commit is contained in:
VladimirTechMan 2018-07-28 18:43:06 -04:00
parent 5dcea1863f
commit 940fdfe787
3 changed files with 231 additions and 14 deletions

View File

@ -389,10 +389,11 @@
return arr;
},
_setChildrenIndices: function() {
this.children.each(function(child, n) {
child.index = n;
});
_setChildrenIndicesBetween: function(idx, maxIdx) {
var children = this.children;
for (; idx <= maxIdx; ++idx) {
children[idx].index = idx;
}
},
drawScene: function(can, top, caching) {
var layer = this.getLayer(),
@ -681,5 +682,6 @@
* });
*/
Konva.Collection.mapMethods(Konva.Container);
})();

View File

@ -609,7 +609,7 @@
if (parent && parent.children) {
parent.children.splice(this.index, 1);
parent._setChildrenIndices();
parent._setChildrenIndicesBetween(this.index, parent.children.length - 1);
delete this.parent;
}
@ -1089,10 +1089,11 @@
Konva.Util.warn('Node has no parent. moveToTop function is ignored.');
return false;
}
var children = this.parent.children;
var index = this.index;
this.parent.children.splice(index, 1);
this.parent.children.push(this);
this.parent._setChildrenIndices();
children.splice(index, 1);
children.push(this);
this.parent._setChildrenIndicesBetween(index, children.length - 1);
return true;
},
/**
@ -1111,7 +1112,7 @@
if (index < len - 1) {
this.parent.children.splice(index, 1);
this.parent.children.splice(index + 1, 0, this);
this.parent._setChildrenIndices();
this.parent._setChildrenIndicesBetween(index, index + 1);
return true;
}
return false;
@ -1131,7 +1132,7 @@
if (index > 0) {
this.parent.children.splice(index, 1);
this.parent.children.splice(index - 1, 0, this);
this.parent._setChildrenIndices();
this.parent._setChildrenIndicesBetween(index - 1, index);
return true;
}
return false;
@ -1153,7 +1154,7 @@
if (index > 0) {
this.parent.children.splice(index, 1);
this.parent.children.unshift(this);
this.parent._setChildrenIndices();
this.parent._setChildrenIndicesBetween(0, index);
return true;
}
return false;
@ -1170,10 +1171,19 @@
Konva.Util.warn('Node has no parent. zIndex parameter is ignored.');
return false;
}
var children = this.parent.children;
zIndex = Math.max(0, Math.min(zIndex, children.length - 1));
var index = this.index;
this.parent.children.splice(index, 1);
this.parent.children.splice(zIndex, 0, this);
this.parent._setChildrenIndices();
if (zIndex !== index) {
children.splice(index, 1);
children.splice(zIndex, 0, this);
if (index < zIndex) {
this.parent._setChildrenIndicesBetween(index, zIndex);
} else {
this.parent._setChildrenIndicesBetween(zIndex, index);
}
}
return this;
},
/**

View File

@ -3479,3 +3479,208 @@ suite('Node', function() {
assert.equal(rect.findAncestor(), null, 'return null if no selector');
});
});
test('moveToTop() properly changes z-indices of the node and its siblings', function() {
var stage = addStage();
var layer = new Konva.Layer();
stage.add(layer);
var rect1 = new Konva.Rect();
var rect2 = new Konva.Rect();
var rect3 = new Konva.Rect();
var rect4 = new Konva.Rect();
layer.add(rect1, rect2, rect3, rect4);
assert.equal(rect1.getZIndex(), 0);
assert.equal(rect2.getZIndex(), 1);
assert.equal(rect3.getZIndex(), 2);
assert.equal(rect4.getZIndex(), 3);
rect2.moveToTop();
assert.equal(rect1.getZIndex(), 0);
assert.equal(rect3.getZIndex(), 1);
assert.equal(rect4.getZIndex(), 2);
assert.equal(rect2.getZIndex(), 3);
rect1.moveToTop();
assert.equal(rect3.getZIndex(), 0);
assert.equal(rect4.getZIndex(), 1);
assert.equal(rect2.getZIndex(), 2);
assert.equal(rect1.getZIndex(), 3);
});
test('moveToBottom() properly changes z-indices of the node and its siblings', function() {
var stage = addStage();
var layer = new Konva.Layer();
stage.add(layer);
var rect1 = new Konva.Rect();
var rect2 = new Konva.Rect();
var rect3 = new Konva.Rect();
var rect4 = new Konva.Rect();
layer.add(rect1, rect2, rect3, rect4);
assert.equal(rect1.getZIndex(), 0);
assert.equal(rect2.getZIndex(), 1);
assert.equal(rect3.getZIndex(), 2);
assert.equal(rect4.getZIndex(), 3);
rect3.moveToBottom();
assert.equal(rect3.getZIndex(), 0);
assert.equal(rect1.getZIndex(), 1);
assert.equal(rect2.getZIndex(), 2);
assert.equal(rect4.getZIndex(), 3);
rect4.moveToBottom();
assert.equal(rect4.getZIndex(), 0);
assert.equal(rect3.getZIndex(), 1);
assert.equal(rect1.getZIndex(), 2);
assert.equal(rect2.getZIndex(), 3);
});
test('moveUp() properly changes z-indices of the node and its siblings', function() {
var stage = addStage();
var layer = new Konva.Layer();
stage.add(layer);
var rect1 = new Konva.Rect();
var rect2 = new Konva.Rect();
var rect3 = new Konva.Rect();
var rect4 = new Konva.Rect();
layer.add(rect1, rect2, rect3, rect4);
assert.equal(rect1.getZIndex(), 0);
assert.equal(rect2.getZIndex(), 1);
assert.equal(rect3.getZIndex(), 2);
assert.equal(rect4.getZIndex(), 3);
rect1.moveUp();
assert.equal(rect2.getZIndex(), 0);
assert.equal(rect1.getZIndex(), 1);
assert.equal(rect3.getZIndex(), 2);
assert.equal(rect4.getZIndex(), 3);
rect3.moveUp();
assert.equal(rect2.getZIndex(), 0);
assert.equal(rect1.getZIndex(), 1);
assert.equal(rect4.getZIndex(), 2);
assert.equal(rect3.getZIndex(), 3);
});
test('moveDown() properly changes z-indices of the node and its siblings', function() {
var stage = addStage();
var layer = new Konva.Layer();
stage.add(layer);
var rect1 = new Konva.Rect();
var rect2 = new Konva.Rect();
var rect3 = new Konva.Rect();
var rect4 = new Konva.Rect();
layer.add(rect1, rect2, rect3, rect4);
assert.equal(rect1.getZIndex(), 0);
assert.equal(rect2.getZIndex(), 1);
assert.equal(rect3.getZIndex(), 2);
assert.equal(rect4.getZIndex(), 3);
rect4.moveDown();
assert.equal(rect1.getZIndex(), 0);
assert.equal(rect2.getZIndex(), 1);
assert.equal(rect4.getZIndex(), 2);
assert.equal(rect3.getZIndex(), 3);
rect2.moveDown();
assert.equal(rect2.getZIndex(), 0);
assert.equal(rect1.getZIndex(), 1);
assert.equal(rect4.getZIndex(), 2);
assert.equal(rect3.getZIndex(), 3);
});
test('setZIndex() properly changes z-indices of the node and its siblings', function() {
var stage = addStage();
var layer = new Konva.Layer();
stage.add(layer);
var rect1 = new Konva.Rect();
var rect2 = new Konva.Rect();
var rect3 = new Konva.Rect();
var rect4 = new Konva.Rect();
layer.add(rect1, rect2, rect3, rect4);
assert.equal(rect1.getZIndex(), 0);
assert.equal(rect2.getZIndex(), 1);
assert.equal(rect3.getZIndex(), 2);
assert.equal(rect4.getZIndex(), 3);
rect1.setZIndex(2);
assert.equal(rect2.getZIndex(), 0);
assert.equal(rect3.getZIndex(), 1);
assert.equal(rect1.getZIndex(), 2);
assert.equal(rect4.getZIndex(), 3);
rect2.setZIndex(3);
assert.equal(rect3.getZIndex(), 0);
assert.equal(rect1.getZIndex(), 1);
assert.equal(rect4.getZIndex(), 2);
assert.equal(rect2.getZIndex(), 3);
rect2.setZIndex(1);
assert.equal(rect3.getZIndex(), 0);
assert.equal(rect2.getZIndex(), 1);
assert.equal(rect1.getZIndex(), 2);
assert.equal(rect4.getZIndex(), 3);
rect4.setZIndex(0);
assert.equal(rect4.getZIndex(), 0);
assert.equal(rect3.getZIndex(), 1);
assert.equal(rect2.getZIndex(), 2);
assert.equal(rect1.getZIndex(), 3);
});
test('remove() removes the node and properly changes z-indices of its siblings', function() {
var stage = addStage();
var layer = new Konva.Layer();
stage.add(layer);
var rect1 = new Konva.Rect();
var rect2 = new Konva.Rect();
var rect3 = new Konva.Rect();
var rect4 = new Konva.Rect();
layer.add(rect1, rect2, rect3, rect4);
assert.equal(layer.getChildren().length, 4);
assert.equal(rect1.getZIndex(), 0);
assert.equal(rect2.getZIndex(), 1);
assert.equal(rect3.getZIndex(), 2);
assert.equal(rect4.getZIndex(), 3);
rect4.remove();
assert.equal(layer.getChildren().length, 3);
assert.equal(rect1.getZIndex(), 0);
assert.equal(rect2.getZIndex(), 1);
assert.equal(rect3.getZIndex(), 2);
rect2.remove();
assert.equal(layer.getChildren().length, 2);
assert.equal(rect1.getZIndex(), 0);
assert.equal(rect3.getZIndex(), 1);
rect1.remove();
assert.equal(layer.getChildren().length, 1);
assert.equal(rect3.getZIndex(), 0);
});