From 2e878aa79b4dafc6c6387c7856663081cec4174e Mon Sep 17 00:00:00 2001 From: Anton Lavrenov Date: Thu, 15 Mar 2018 12:11:58 +0700 Subject: [PATCH] bugs fixes --- src/shapes/Transformer.js | 228 ++++++++++++++++++++++++--- test/unit/shapes/Transformer-test.js | 52 +++++- 2 files changed, 255 insertions(+), 25 deletions(-) diff --git a/src/shapes/Transformer.js b/src/shapes/Transformer.js index 2a032cec..1d47f079 100644 --- a/src/shapes/Transformer.js +++ b/src/shapes/Transformer.js @@ -6,6 +6,8 @@ 'rotateHandlerOffsetChange' ].join(' '); + var NODE_RECT = 'nodeRect'; + var TRANSFORM_CHANGE_STR = [ 'xChange.resizer', 'yChange.resizer', @@ -75,6 +77,31 @@ } } + /** + * Transformer constructor. Transformer is a special type of group that allow you transform Konva + * primitives and shapes. + * @constructor + * @memberof Konva + * @augments Konva.Container + * @param {Object} config + * @param {Boolean} [config.resizeEnabled] Default is true + * @param {Boolean} [config.rotateEnabled] Default is true + * @param {Array} [config.rotationSnaps] Array of angles for rotation snaps. Default is [] + * @param {Number} [config.rotateHandlerOffset] Default is 50 + * @param {Number} [config.lineEnabled] Should we draw border? Default is true + * @param {Boolean} [config.keepRatio] Should we keep ratio when we are moving edges? Default is true + * @param {Array} [config.enabledHandlers] Array of names of enabled handles + * @@nodeParams + * @@containerParams + * @example + * var transformer = new Konva.Transformer({ + * node: rectangle, + * rotateHandlerOffset: 60, + * enabledHandlers: ['top-left', 'top-right', 'bottom-left', 'bottom-right'] + * }); + * layer.add(transformer); + */ + Konva.Transformer = function(config) { this.____init(config); }; @@ -114,17 +141,39 @@ ); warningShowed = true; } + + if (this.getNode()) { + this.update(); + } }, attachTo: function(node) { - if (this.node()) { + this.setNode(node); + }, + + setNode(node) { + if (this._node) { this.detach(); } - this.setNode(node); - node.on('dragmove.resizer', this.update); - node.on(TRANSFORM_CHANGE_STR, this.update); + this._node = node; - this.update(); + node.on( + TRANSFORM_CHANGE_STR, + function() { + this._clearCache(NODE_RECT); + }.bind(this) + ); + node.on(TRANSFORM_CHANGE_STR, this.requestUpdate.bind(this)); + node.on('dragmove.resizer', this.requestUpdate); + + var elementsCreated = !!this.findOne('.top-left'); + if (elementsCreated) { + this.update(); + } + }, + + getNode() { + return this._node; }, detach: function() { @@ -132,7 +181,20 @@ }, _getNodeRect: function() { + return this._getCache(NODE_RECT, this.__getNodeRect); + }, + + __getNodeRect: function() { var node = this.getNode(); + if (!node) { + return { + x: -Number.MAX_SAFE_INTEGER, + y: -Number.MAX_SAFE_INTEGER, + width: 0, + height: 0, + rotation: 0 + }; + } var rect = node.getClientRect({ skipTransform: true }); var rotation = Konva.getAngle(node.rotation()); @@ -484,6 +546,23 @@ this.update(); this.getLayer().batchDraw(); }, + + requestUpdate: function() { + if (this.timeout) { + return; + } + this.timeout = setTimeout( + function() { + this.timeout = null; + this.update(); + }.bind(this) + ); + }, + + forceUpdate: function() { + this._clearCache(NODE_RECT); + this.update(); + }, update: function() { var attrs = this._getNodeRect(); var x = attrs.x; @@ -494,48 +573,48 @@ this.y(y); this.rotation(attrs.rotation); - var enabledResizers = this.enabledResizers(); + var enabledHandlers = this.enabledHandlers(); var resizeEnabled = this.resizeEnabled(); this.findOne('.top-left').setAttrs({ x: 0, y: 0, - visible: resizeEnabled && enabledResizers.indexOf('top-left') >= 0 + visible: resizeEnabled && enabledHandlers.indexOf('top-left') >= 0 }); this.findOne('.top-center').setAttrs({ x: width / 2, y: 0, - visible: resizeEnabled && enabledResizers.indexOf('top-center') >= 0 + visible: resizeEnabled && enabledHandlers.indexOf('top-center') >= 0 }); this.findOne('.top-right').setAttrs({ x: width, y: 0, - visible: resizeEnabled && enabledResizers.indexOf('top-right') >= 0 + visible: resizeEnabled && enabledHandlers.indexOf('top-right') >= 0 }); this.findOne('.middle-left').setAttrs({ x: 0, y: height / 2, - visible: resizeEnabled && enabledResizers.indexOf('middle-left') >= 0 + visible: resizeEnabled && enabledHandlers.indexOf('middle-left') >= 0 }); this.findOne('.middle-right').setAttrs({ x: width, y: height / 2, - visible: resizeEnabled && enabledResizers.indexOf('middle-right') >= 0 + visible: resizeEnabled && enabledHandlers.indexOf('middle-right') >= 0 }); this.findOne('.bottom-left').setAttrs({ x: 0, y: height, - visible: resizeEnabled && enabledResizers.indexOf('bottom-left') >= 0 + visible: resizeEnabled && enabledHandlers.indexOf('bottom-left') >= 0 }); this.findOne('.bottom-center').setAttrs({ x: width / 2, y: height, - visible: resizeEnabled && enabledResizers.indexOf('bottom-center') >= 0 + visible: resizeEnabled && enabledHandlers.indexOf('bottom-center') >= 0 }); this.findOne('.bottom-right').setAttrs({ x: width, y: height, - visible: resizeEnabled && enabledResizers.indexOf('bottom-right') >= 0 + visible: resizeEnabled && enabledHandlers.indexOf('bottom-right') >= 0 }); this.findOne('.rotater').setAttrs({ @@ -554,13 +633,18 @@ Konva.Group.prototype.destroy.call(this); this.getNode().off('.resizer'); this._removeEvents(); + }, + // do not work as a container + // we will recreate inner nodes manually + toObject: function() { + return Konva.Node.prototype.toObject.call(this); } }; Konva.Util.extend(Konva.Transformer, Konva.Group); function validateResizers(val) { if (!(val instanceof Array)) { - Konva.Util.warn('enabledResizers value should be an array'); + Konva.Util.warn('enabledHandlers value should be an array'); } if (val instanceof Array) { val.forEach(function(name) { @@ -576,25 +660,125 @@ } return val || []; } + + /** + * get/set enabled handlers + * @name enabledHandlers + * @method + * @memberof Konva.Transformer.prototype + * @param {Array} array + * @returns {Array} + * @example + * // get list of handlers + * var enabledHandlers = shape.enabledHandlers(); + * + * // set handlers + * shape.enabledHandlers(['top-left', 'top-center', 'top-right', 'middle-right', 'middle-left', 'bottom-left', 'bottom-center', 'bottom-right']); + */ Konva.Factory.addGetterSetter( Konva.Transformer, - 'enabledResizers', - RESIZERS_NAMES - ); - Konva.Factory.addGetterSetter( - Konva.Transformer, - 'enabledResizers', + 'enabledHandlers', RESIZERS_NAMES, validateResizers ); + + /** + * get/set resize ability. If false it will automatically hide resizing handlers + * @name resizeEnabled + * @method + * @memberof Konva.Transformer.prototype + * @param {Array} array + * @returns {Array} + * @example + * // get + * var resizeEnabled = shape.resizeEnabled(); + * + * // set + * shape.resizeEnabled(false); + */ Konva.Factory.addGetterSetter(Konva.Transformer, 'resizeEnabled', true); + + /** + * get/set ability to rotate. + * @name rotateEnabled + * @method + * @memberof Konva.Transformer.prototype + * @param {Array} array + * @returns {Array} + * @example + * // get + * var rotateEnabled = shape.rotateEnabled(); + * + * // set + * shape.rotateEnabled(false); + */ Konva.Factory.addGetterSetter(Konva.Transformer, 'rotateEnabled', true); + + /** + * get/set rotation snaps angles. + * @name rotationSnaps + * @method + * @memberof Konva.Transformer.prototype + * @param {Array} array + * @returns {Array} + * @example + * // get + * var rotationSnaps = shape.rotationSnaps(); + * + * // set + * shape.rotationSnaps([0, 90, 180, 270]); + */ Konva.Factory.addGetterSetter(Konva.Transformer, 'rotationSnaps', []); + + /** + * get/set distance for rotation handler + * @name rotateHandlerOffset + * @method + * @memberof Konva.Transformer.prototype + * @param {Array} array + * @returns {Array} + * @example + * // get + * var rotateHandlerOffset = shape.rotateHandlerOffset(); + * + * // set + * shape.rotateHandlerOffset(100); + */ Konva.Factory.addGetterSetter(Konva.Transformer, 'rotateHandlerOffset', 50); + + /** + * get/set visibility of border + * @name lineEnabled + * @method + * @memberof Konva.Transformer.prototype + * @param {Array} array + * @returns {Array} + * @example + * // get + * var lineEnabled = shape.lineEnabled(); + * + * // set + * shape.lineEnabled(false); + */ Konva.Factory.addGetterSetter(Konva.Transformer, 'lineEnabled', true); + + /** + * get/set should we keep ration of resize? + * @name keepRatio + * @method + * @memberof Konva.Transformer.prototype + * @param {Array} array + * @returns {Array} + * @example + * // get + * var keepRatio = shape.keepRatio(); + * + * // set + * shape.keepRatio(false); + */ Konva.Factory.addGetterSetter(Konva.Transformer, 'keepRatio', true); - Konva.Factory.addGetterSetter(Konva.Transformer, 'node'); + Konva.Factory.addOverloadedGetterSetter(Konva.Transformer, 'node'); Konva.Collection.mapMethods(Konva.Transformer); })(Konva); diff --git a/test/unit/shapes/Transformer-test.js b/test/unit/shapes/Transformer-test.js index ac634df3..6c872cf9 100644 --- a/test/unit/shapes/Transformer-test.js +++ b/test/unit/shapes/Transformer-test.js @@ -15,9 +15,10 @@ suite('Transformer', function() { }); layer.add(rect); - var tr = new Konva.Transformer(); + var tr = new Konva.Transformer({ + node: rect + }); layer.add(tr); - tr.attachTo(rect); layer.draw(); assert.equal(tr.getClassName(), 'Transformer'); @@ -26,6 +27,12 @@ suite('Transformer', function() { assert.equal(tr.y(), rect.y()); assert.equal(tr.width(), rect.width()); assert.equal(tr.height(), rect.height()); + + // manual check of correct position of node + var handler = tr.findOne('.bottom-right'); + var pos = handler.getAbsolutePosition(); + assert.equal(pos.x, rect.x() + rect.width()); + assert.equal(pos.y, rect.y() + rect.height()); }); test('try to fit simple rectangle', function() { @@ -541,7 +548,7 @@ suite('Transformer', function() { assert.equal(tr.rotation(), 0); }); - test('fit group', function() { + test.only('fit group', function() { var stage = addStage(); var layer = new Konva.Layer(); stage.add(layer); @@ -596,4 +603,43 @@ suite('Transformer', function() { assert.equal(tr.width(), 200); assert.equal(tr.height(), 100); }); + + test('toJSON should not save attached node and children', function() { + var stage = addStage(); + var layer = new Konva.Layer(); + stage.add(layer); + + var rect = new Konva.Rect({ + x: 100, + y: 60, + draggable: true, + width: 100, + height: 100, + fill: 'yellow' + }); + layer.add(rect); + + var tr = new Konva.Transformer(); + layer.add(tr); + tr.attachTo(rect); + + layer.draw(); + + var json = tr.toJSON(); + var object = JSON.parse(json); + + assert.equal(object.attrs.node, undefined); + assert.equal(object.children, undefined); + }); + + test('make sure we can work without inner node', function() { + var stage = addStage(); + var layer = new Konva.Layer(); + stage.add(layer); + + var tr = new Konva.Transformer(); + layer.add(tr); + + layer.draw(); + }); });