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) {