diff --git a/src/shapes/Transformer.js b/src/shapes/Transformer.js index 424e65c2..3500643e 100644 --- a/src/shapes/Transformer.js +++ b/src/shapes/Transformer.js @@ -1,6 +1,9 @@ (function(Konva) { 'use strict'; + var BASE_BOX_WIDTH = 10; + var BASE_BOX_HEIGHT = 10; + var ATTR_CHANGE_LIST = [ 'resizeEnabledChange', 'rotateHandlerOffsetChange' @@ -46,12 +49,17 @@ 'bottom-right': 135 }; - function getCursor(anchorName, rad) { + function getCursor(anchorName, rad, isMirrored) { if (anchorName === 'rotater') { return 'crosshair'; } rad += Konva.Util._degToRad(ANGLES[anchorName] || 0); + // If we are mirrored, we need to mirror the angle (this is not the same as + // rotate). + if (isMirrored) { + rad *= -1; + } var angle = (Konva.Util._radToDeg(rad) % 360 + 360) % 360; if ( @@ -130,8 +138,6 @@ 'bottom-right' ]; - var warningShowed = false; - Konva.Transformer.prototype = { _centroid: false, ____init: function(config) { @@ -148,13 +154,6 @@ // update transformer data for certain attr changes this.on(ATTR_CHANGE_LIST, this.update); - if (!warningShowed) { - Konva.Util.warn( - 'Konva.Transformer is currently experimental and may have bugs. Please report any issues to GitHub repo.' - ); - warningShowed = true; - } - if (this.getNode()) { this.update(); } @@ -299,10 +298,10 @@ fill: 'white', strokeWidth: 1, name: name, - width: 10, - height: 10, - offsetX: 5, - offsetY: 5, + width: BASE_BOX_WIDTH, + height: BASE_BOX_HEIGHT, + offsetX: BASE_BOX_WIDTH / 2, + offsetY: BASE_BOX_HEIGHT / 2, dragDistance: 0 }); var self = this; @@ -334,8 +333,10 @@ // var dy = -pos.y + center.y; // var angle = -Math.atan2(-dy, dx) - Math.PI / 2; - - var cursor = getCursor(name, rad); + var scale = tr.getNode().getAbsoluteScale(); + // If scale.y < 0 xor scale.x < 0 we need to flip (not rotate). + var isMirrored = scale.y * scale.x < 0; + var cursor = getCursor(name, rad, isMirrored); anchor.getStage().content.style.cursor = cursor; layer.batchDraw(); }); @@ -648,6 +649,15 @@ }, update: function() { var attrs = this._getNodeRect(); + var node = this.getNode(); + var scale = { x: 1, y: 1 }; + if (node && node.getParent()) { + scale = node.getParent().getAbsoluteScale(); + } + var invertedScale = { + x: 1 / scale.x, + y: 1 / scale.y + }; var width = attrs.width; var height = attrs.height; @@ -658,53 +668,65 @@ this.findOne('.top-left').setAttrs({ x: -padding, y: -padding, + scale: invertedScale, visible: resizeEnabled && enabledHandlers.indexOf('top-left') >= 0 }); this.findOne('.top-center').setAttrs({ x: width / 2, y: -padding, + scale: invertedScale, visible: resizeEnabled && enabledHandlers.indexOf('top-center') >= 0 }); this.findOne('.top-right').setAttrs({ x: width + padding, y: -padding, + scale: invertedScale, visible: resizeEnabled && enabledHandlers.indexOf('top-right') >= 0 }); this.findOne('.middle-left').setAttrs({ x: -padding, y: height / 2, + scale: invertedScale, visible: resizeEnabled && enabledHandlers.indexOf('middle-left') >= 0 }); this.findOne('.middle-right').setAttrs({ x: width + padding, y: height / 2, + scale: invertedScale, visible: resizeEnabled && enabledHandlers.indexOf('middle-right') >= 0 }); this.findOne('.bottom-left').setAttrs({ x: -padding, y: height + padding, + scale: invertedScale, visible: resizeEnabled && enabledHandlers.indexOf('bottom-left') >= 0 }); this.findOne('.bottom-center').setAttrs({ x: width / 2, y: height + padding, + scale: invertedScale, visible: resizeEnabled && enabledHandlers.indexOf('bottom-center') >= 0 }); this.findOne('.bottom-right').setAttrs({ x: width + padding, y: height + padding, + scale: invertedScale, visible: resizeEnabled && enabledHandlers.indexOf('bottom-right') >= 0 }); + var scaledRotateHandlerOffset = + -this.rotateHandlerOffset() * Math.abs(invertedScale.y); this.findOne('.rotater').setAttrs({ x: width / 2, - y: -this.rotateHandlerOffset() * Konva.Util._sign(height), + y: scaledRotateHandlerOffset * Konva.Util._sign(height), + scale: invertedScale, visible: this.rotateEnabled() }); this.findOne('.back').setAttrs({ - width: width, - height: height, + width: width * scale.x, + height: height * scale.y, + scale: invertedScale, visible: this.lineEnabled() }); }, diff --git a/test/unit/shapes/Transformer-test.js b/test/unit/shapes/Transformer-test.js index 37a49598..f62b0463 100644 --- a/test/unit/shapes/Transformer-test.js +++ b/test/unit/shapes/Transformer-test.js @@ -997,7 +997,38 @@ suite('Transformer', function() { x: 50, y: 1 }); - assert.equal(stage.content.style.cursor, 'nesw-resize'); + assert.equal(stage.content.style.cursor, 'nwse-resize'); + }); + + test('check correct cursor on scaled parent', function() { + var stage = addStage(); + var layer = new Konva.Layer({ + y: 100, + scaleY: -1, + }); + stage.add(layer); + + var rect = new Konva.Rect({ + x: 50, + y: 0, + draggable: true, + width: 100, + height: 100, + fill: 'yellow' + }); + layer.add(rect); + + var tr = new Konva.Transformer({ + node: rect + }); + layer.add(tr); + layer.draw(); + + stage.simulateMouseMove({ + x: 50, + y: 1 + }); + assert.equal(stage.content.style.cursor, 'nwse-resize'); }); test('stopTransform method', function() { @@ -1133,4 +1164,38 @@ suite('Transformer', function() { }); assert.equal(shape.name(), 'top-left'); }); + + test('check rotator size on scaled transformer', function() { + var stage = addStage(); + var layer = new Konva.Layer({ + scaleX: 10, + scaleY: 10 + }); + stage.add(layer); + + var rect = new Konva.Rect({ + x: 5, + y: 16, + draggable: true, + width: 10, + height: 10, + fill: 'yellow', + }); + layer.add(rect); + + var tr = new Konva.Transformer({ + node: rect, + }); + layer.add(tr); + layer.draw(); + + var rotater = tr.findOne('.rotater'); + var pos = rotater.getAbsolutePosition(); + + // pos.x === (x * scaleX - (height)) + assert.equal(pos.x, 100); + + // pos.y === (y * scaleY - (height * scaleY / 2)) + assert.equal(pos.y, 110); + }); });