diff --git a/src/Container.js b/src/Container.js index bc9ed803..7e65fcff 100644 --- a/src/Container.js +++ b/src/Container.js @@ -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); })(); diff --git a/src/Node.js b/src/Node.js index 9a2a60dc..2d1f54d1 100644 --- a/src/Node.js +++ b/src/Node.js @@ -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; }, /** diff --git a/test/unit/Node-test.js b/test/unit/Node-test.js index a339bd43..ef4a12e6 100644 --- a/test/unit/Node-test.js +++ b/test/unit/Node-test.js @@ -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); +});