From 5f9edecd1898cca943ac8e5d741ce79546329c9f Mon Sep 17 00:00:00 2001 From: Kienz Date: Thu, 26 Sep 2013 18:19:17 +0200 Subject: [PATCH] [BACK_INCOMPAT] Split `fabric.Object.centerTransform` and `fabric.Canvas.centerTransform` into the properties `centeredScaling` and `centeredRotation` centeredScaling: Define if object scaling should be centered (true). Default = false. centeredRotation: Define if object rotates around center (true) or around origin (originX/originY) point (false). Default = false. Pressed altKey negates the behavior. --- src/canvas.class.js | 98 +++++++++++++++++++++---------- src/mixins/canvas_events.mixin.js | 51 ++++++++++------ src/shapes/object.class.js | 27 ++++++--- 3 files changed, 120 insertions(+), 56 deletions(-) diff --git a/src/canvas.class.js b/src/canvas.class.js index 7edd023b..86f6d6a7 100644 --- a/src/canvas.class.js +++ b/src/canvas.class.js @@ -48,11 +48,22 @@ uniScaleTransform: false, /** - * When true, objects use center point as the origin of transformation + * When true, objects use center point as the origin of scale transformation. + * Backwards incompatibility note: This property replaces "centerTransform" (Boolean). + * @since 1.3.4 * @type Boolean * @default */ - centerTransform: false, + centeredScaling: false, + + /** + * When true, objects use center point as the origin of rotate transformation. + * Backwards incompatibility note: This property replaces "centerTransform" (Boolean). + * @since 1.3.4 + * @type Boolean + * @default + */ + centeredRotation: false, /** * Indicates that canvas is interactive. This property should not be changed. @@ -80,7 +91,7 @@ * If not empty the selection border is dashed * @type Array */ - selectionDashArray: [ ], + selectionDashArray: [ ], /** * Color of the border of selection (usually slightly darker than color of selection itself) @@ -136,7 +147,7 @@ * @type String * @default */ - containerClass: 'canvas-container', + containerClass: 'canvas-container', /** * When true, object detection happens on per-pixel basis rather than on per-bounding-box @@ -157,7 +168,7 @@ * @type Boolean * @default */ - skipTargetFind: false, + skipTargetFind: false, /** * @private @@ -182,31 +193,38 @@ _resetCurrentTransform: function(e) { var t = this._currentTransform; - t.target.set('scaleX', t.original.scaleX); - t.target.set('scaleY', t.original.scaleY); - t.target.set('left', t.original.left); - t.target.set('top', t.original.top); + t.target.set({ + 'scaleX': t.original.scaleX, + 'scaleY': t.original.scaleY, + 'left': t.original.left, + 'top': t.original.top + }); - if (e.altKey || this.centerTransform || t.target.centerTransform) { - if (t.originX !== 'center') { - if (t.originX === 'right') { - t.mouseXSign = -1; - } - else { - t.mouseXSign = 1; - } + if (this._shouldCenterTransform(e, t.target)) { + if (t.action === 'rotate') { + this._setOriginToCenter(t.target); } - if (t.originY !== 'center') { - if (t.originY === 'bottom') { - t.mouseYSign = -1; + else { + if (t.originX !== 'center') { + if (t.originX === 'right') { + t.mouseXSign = -1; + } + else { + t.mouseXSign = 1; + } } - else { - t.mouseYSign = 1; + if (t.originY !== 'center') { + if (t.originY === 'bottom') { + t.mouseYSign = -1; + } + else { + t.mouseYSign = 1; + } } - } - t.originX = 'center'; - t.originY = 'center'; + t.originX = 'center'; + t.originY = 'center'; + } } else { t.originX = t.original.originX; @@ -323,6 +341,27 @@ ); }, + /** + * @private + * @param {Event} e Event object + * @param {fabric.Object} target + */ + _shouldCenterTransform: function (e, target) { + if (!target) return; + + var t = this._currentTransform, + centerTransform; + + if (t.action === 'scale' || t.action === 'scaleX' || t.action === 'scaleY') { + centerTransform = this.centeredScaling || target.centeredScaling; + } + else if (t.action === 'rotate') { + centerTransform = this.centeredRotation || target.centeredRotation; + } + + return centerTransform ? !e.altKey : e.altKey; + }, + /** * @private * @param {Event} e Event object @@ -346,7 +385,8 @@ : 'scale'; } - var originX = target.originX, originY = target.originY; + var originX = target.originX, + originY = target.originY; if (corner === 'ml' || corner === 'tl' || corner === 'bl') { originX = "right"; @@ -362,12 +402,6 @@ originY = "top"; } - // if (corner === 'mtr') { - // originX = 'center'; - // originY = 'center'; - // } - - // var center = target.getCenterPoint(); this._currentTransform = { target: target, action: action, diff --git a/src/mixins/canvas_events.mixin.js b/src/mixins/canvas_events.mixin.js index 04496d02..ea70a739 100644 --- a/src/mixins/canvas_events.mixin.js +++ b/src/mixins/canvas_events.mixin.js @@ -138,7 +138,6 @@ * @param {Event} e Event object fired on mouseup */ __onMouseUp: function (e) { - var target, pointer, render; @@ -249,7 +248,6 @@ * @param {Event} e Event object fired on mousedown */ __onMouseDown: function (e) { - // accept only left clicks var isLeftClick = 'which' in e ? e.which === 1 : e.button === 1; if (!isLeftClick && !fabric.isTouchSupported) return; @@ -308,17 +306,13 @@ this.fire('mouse:down', { target: target, e: e }); target && target.fire('mousedown', { e: e }); - - if (corner === 'mtr' && target.centerTransform) { - this._setOriginToCenter(target); - } }, /** * @private + * @param {Object} target Object for that origin is set to center */ _setOriginToCenter: function(target) { - this._previousOriginX = this._currentTransform.target.originX; this._previousOriginY = this._currentTransform.target.originY; @@ -334,6 +328,26 @@ this._currentTransform.top = target.top; }, + /** + * @private + * @param {Object} target Object for that center is set to origin + */ + _setCenterToOrigin: function(target) { + var originPoint = target.translateToOriginPoint( + target.getCenterPoint(), + this._previousOriginX, + this._previousOriginY); + + target.originX = this._previousOriginX; + target.originY = this._previousOriginY; + + target.left = originPoint.x; + target.top = originPoint.y; + + this._previousOriginX = null; + this._previousOriginY = null; + }, + /** * Method that defines the actions when mouse is hovering the canvas. * The currentTransform parameter will definde whether the user is rotating/scaling/translating @@ -344,7 +358,6 @@ * @param {Event} e Event object fired on mousemove */ __onMouseMove: function (e) { - var target, pointer; if (this.isDrawingMode) { @@ -394,19 +407,23 @@ var x = pointer.x, y = pointer.y, reset = false, + centerTransform, transform = this._currentTransform; target = transform.target; target.isMoving = true; - if ((transform.action === 'scale' || transform.action === 'scaleX' || transform.action === 'scaleY') && - // Switch from a normal resize to center-based - ((e.altKey && (transform.originX !== 'center' || transform.originY !== 'center')) || - // Switch from center-based resize to normal one - (!e.altKey && transform.originX === 'center' && transform.originY === 'center')) - ) { - this._resetCurrentTransform(e); - reset = true; + if (transform.action === 'scale' || transform.action === 'scaleX' || transform.action === 'scaleY') { + centerTransform = this._shouldCenterTransform(e, target); + + // Switch from a normal resize to center-based + if ((centerTransform && (transform.originX !== 'center' || transform.originY !== 'center')) || + // Switch from center-based resize to normal one + (!centerTransform && transform.originX === 'center' && transform.originY === 'center') + ) { + this._resetCurrentTransform(e); + reset = true; + } } if (transform.action === 'rotate') { @@ -425,7 +442,7 @@ else { // Switch from a normal resize to proportional if (!reset && transform.currentAction === 'scale') { - this._resetCurrentTransform(e); + this._resetCurrentTransform(e, target); } transform.currentAction = 'scaleEqually'; diff --git a/src/shapes/object.class.js b/src/shapes/object.class.js index 8a3b4bf1..71fe0982 100644 --- a/src/shapes/object.class.js +++ b/src/shapes/object.class.js @@ -154,10 +154,23 @@ /** * When true, this object will use center point as the origin of transformation - * when being resized via the controls + * when being scaled via the controls. + * Backwards incompatibility note: This property replaces "centerTransform" (Boolean). + * @since 1.3.4 * @type Boolean + * @default */ - centerTransform: false, + centeredScaling: false, + + /** + * When true, this object will use center point as the origin of transformation + * when being rotated via the controls. + * Backwards incompatibility note: This property replaces "centerTransform" (Boolean). + * @since 1.3.4 + * @type Boolean + * @default + */ + centeredRotation: false, /** * Color of object's fill @@ -264,7 +277,7 @@ selectable: true, /** - * When set to `false`, an object can not be a target of events. All events propagate through it. Introduced in v1.3.4 + * When set to `false`, an object can not be a target of events. All events propagate through it. * @type Boolean * @default */ @@ -872,10 +885,10 @@ * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png" * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg. * @param {Number} [options.multiplier=1] Multiplier to scale by - * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14 - * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14 - * @param {Number} [options.width] Cropping width. Introduced in v1.2.14 - * @param {Number} [options.height] Cropping height. Introduced in v1.2.14 + * @param {Number} [options.left] Cropping left offset + * @param {Number} [options.top] Cropping top offset + * @param {Number} [options.width] Cropping width + * @param {Number} [options.height] Cropping height * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format */ toDataURL: function(options) {