mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-05-11 07:13:09 +00:00
Add support for skew objects.
This commit is contained in:
parent
7a8c69e0ff
commit
cda0611326
24 changed files with 367 additions and 98 deletions
|
|
@ -213,17 +213,19 @@
|
|||
* @private
|
||||
* @param {Event} e Event object fired on mousemove
|
||||
*/
|
||||
_resetCurrentTransform: function(e) {
|
||||
_resetCurrentTransform: function() {
|
||||
var t = this._currentTransform;
|
||||
|
||||
t.target.set({
|
||||
scaleX: t.original.scaleX,
|
||||
scaleY: t.original.scaleY,
|
||||
skewX: t.original.skewX,
|
||||
skewY: t.original.skewY,
|
||||
left: t.original.left,
|
||||
top: t.original.top
|
||||
});
|
||||
|
||||
if (this._shouldCenterTransform(e, t.target)) {
|
||||
if (this._shouldCenterTransform(t.target)) {
|
||||
if (t.action === 'rotate') {
|
||||
this._setOriginToCenter(t.target);
|
||||
}
|
||||
|
|
@ -348,10 +350,9 @@
|
|||
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} e Event object
|
||||
* @param {fabric.Object} target
|
||||
*/
|
||||
_shouldCenterTransform: function (e, target) {
|
||||
_shouldCenterTransform: function (target) {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -366,7 +367,7 @@
|
|||
centerTransform = this.centeredRotation || target.centeredRotation;
|
||||
}
|
||||
|
||||
return centerTransform ? !e.altKey : e.altKey;
|
||||
return centerTransform ? !t.altKey : t.altKey;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -398,18 +399,23 @@
|
|||
/**
|
||||
* @private
|
||||
*/
|
||||
_getActionFromCorner: function(target, corner) {
|
||||
var action = 'drag';
|
||||
if (corner) {
|
||||
action = (corner === 'ml' || corner === 'mr')
|
||||
? 'scaleX'
|
||||
: (corner === 'mt' || corner === 'mb')
|
||||
? 'scaleY'
|
||||
: corner === 'mtr'
|
||||
? 'rotate'
|
||||
: 'scale';
|
||||
_getActionFromCorner: function(target, corner, e) {
|
||||
if (!corner) {
|
||||
return 'drag';
|
||||
}
|
||||
|
||||
switch (corner) {
|
||||
case 'mtr':
|
||||
return 'rotate';
|
||||
case 'ml':
|
||||
case 'mr':
|
||||
return e.shiftKey ? 'skewY' : 'scaleX';
|
||||
case 'mt':
|
||||
case 'mb':
|
||||
return e.shiftKey ? 'skewX' : 'scaleY';
|
||||
default:
|
||||
return 'scale';
|
||||
}
|
||||
return action;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -424,26 +430,33 @@
|
|||
|
||||
var pointer = this.getPointer(e),
|
||||
corner = target._findTargetCorner(this.getPointer(e, true)),
|
||||
action = this._getActionFromCorner(target, corner),
|
||||
action = this._getActionFromCorner(target, corner, e),
|
||||
origin = this._getOriginFromCorner(target, corner);
|
||||
|
||||
this._currentTransform = {
|
||||
target: target,
|
||||
action: action,
|
||||
corner: corner,
|
||||
scaleX: target.scaleX,
|
||||
scaleY: target.scaleY,
|
||||
skewX: target.skewX,
|
||||
skewY: target.skewY,
|
||||
offsetX: pointer.x - target.left,
|
||||
offsetY: pointer.y - target.top,
|
||||
originX: origin.x,
|
||||
originY: origin.y,
|
||||
ex: pointer.x,
|
||||
ey: pointer.y,
|
||||
lastX: pointer.x,
|
||||
lastY: pointer.y,
|
||||
left: target.left,
|
||||
top: target.top,
|
||||
theta: degreesToRadians(target.angle),
|
||||
width: target.width * target.scaleX,
|
||||
mouseXSign: 1,
|
||||
mouseYSign: 1
|
||||
mouseYSign: 1,
|
||||
shiftKey: e.shiftKey,
|
||||
altKey: e.altKey
|
||||
};
|
||||
|
||||
this._currentTransform.original = {
|
||||
|
|
@ -451,11 +464,13 @@
|
|||
top: target.top,
|
||||
scaleX: target.scaleX,
|
||||
scaleY: target.scaleY,
|
||||
skewX: target.skewX,
|
||||
skewY: target.skewY,
|
||||
originX: origin.x,
|
||||
originY: origin.y
|
||||
};
|
||||
|
||||
this._resetCurrentTransform(e);
|
||||
this._resetCurrentTransform();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -475,6 +490,114 @@
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if we are increasing a positive skew or lower it,
|
||||
* checking mouse direction and pressed corner.
|
||||
* @private
|
||||
*/
|
||||
_changeSkewTransformOrigin: function(mouseMove, t, by) {
|
||||
var property = 'originX', origins = { 0: 'center' },
|
||||
skew = t.target.skewX, originA = 'left', originB = 'right',
|
||||
corner = t.corner === 'mt' || t.corner === 'ml' ? 1 : -1,
|
||||
flipSign = 1;
|
||||
|
||||
mouseMove = mouseMove > 0 ? 1 : -1;
|
||||
if (by === 'y') {
|
||||
skew = t.target.skewY;
|
||||
originA = 'top';
|
||||
originB = 'bottom';
|
||||
property = 'originY';
|
||||
}
|
||||
origins[-1] = originA;
|
||||
origins[1] = originB;
|
||||
|
||||
t.target.flipX && (flipSign *= -1);
|
||||
t.target.flipY && (flipSign *= -1);
|
||||
|
||||
if (skew === 0) {
|
||||
t.skewSign = -corner * mouseMove * flipSign;
|
||||
t[property] = origins[-mouseMove];
|
||||
}
|
||||
else {
|
||||
skew = skew > 0 ? 1 : -1;
|
||||
t.skewSign = skew;
|
||||
t[property] = origins[skew * corner * flipSign];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Skew object by mouse events
|
||||
* @private
|
||||
* @param {Number} x pointer's x coordinate
|
||||
* @param {Number} y pointer's y coordinate
|
||||
* @param {String} by Either 'x' or 'y'
|
||||
*/
|
||||
_skewObject: function (x, y, by) {
|
||||
var t = this._currentTransform,
|
||||
target = t.target,
|
||||
lockSkewingX = target.get('lockSkewingX'),
|
||||
lockSkewingY = target.get('lockSkewingY');
|
||||
|
||||
if ((lockSkewingX && by === 'x') || (lockSkewingY && by === 'y')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the constraint point
|
||||
var center = target.getCenterPoint(),
|
||||
actualMouseByCenter = target.toLocalPoint(new fabric.Point(x, y), 'center', 'center')[by],
|
||||
lastMouseByCenter = target.toLocalPoint(new fabric.Point(t.lastX, t.lastY), 'center', 'center')[by],
|
||||
actualMouseByOrigin, constraintPosition, dim = target._getTransformedDimensions();
|
||||
|
||||
this._changeSkewTransformOrigin(actualMouseByCenter - lastMouseByCenter, t, by);
|
||||
actualMouseByOrigin = target.toLocalPoint(new fabric.Point(x, y), t.originX, t.originY)[by],
|
||||
|
||||
constraintPosition = target.translateToOriginPoint(center, t.originX, t.originY);
|
||||
// Actually skew the object
|
||||
this._setObjectSkew(actualMouseByOrigin, t, by, dim);
|
||||
t.lastX = x;
|
||||
t.lastY = y;
|
||||
// Make sure the constraints apply
|
||||
target.setPositionByOrigin(constraintPosition, t.originX, t.originY);
|
||||
},
|
||||
|
||||
_setObjectSkew: function(localMouse, transform, by, _dim) {
|
||||
var target = transform.target, newValue,
|
||||
skewSign = transform.skewSign, newDim, dimNoSkew,
|
||||
otherBy, _otherBy, _by, newDimMouse, skewX, skewY;
|
||||
|
||||
if (by === 'x') {
|
||||
otherBy = 'y';
|
||||
_otherBy = 'Y';
|
||||
_by = 'X';
|
||||
skewX = 0;
|
||||
skewY = target.skewY;
|
||||
}
|
||||
else {
|
||||
otherBy = 'x';
|
||||
_otherBy = 'X';
|
||||
_by = 'Y';
|
||||
skewX = target.skewX;
|
||||
skewY = 0;
|
||||
}
|
||||
|
||||
dimNoSkew = target._getTransformedDimensions(skewX, skewY);
|
||||
newDimMouse = 2 * Math.abs(localMouse) - dimNoSkew[by];
|
||||
if (newDimMouse <= 2) {
|
||||
newValue = 0;
|
||||
}
|
||||
else {
|
||||
newValue = skewSign * Math.atan((newDimMouse / target['scale' + _by]) /
|
||||
(dimNoSkew[otherBy] / target['scale' + _otherBy]));
|
||||
newValue = fabric.util.radiansToDegrees(newValue);
|
||||
}
|
||||
target.set('skew' + _by, newValue);
|
||||
if (target['skew' + _otherBy] !== 0) {
|
||||
newDim = target._getTransformedDimensions();
|
||||
newValue = (_dim[otherBy] / newDim[otherBy]) * target['scale' + _otherBy];
|
||||
target.set('scale' + _otherBy, newValue);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Scales object by invoking its scaleX/scaleY methods
|
||||
* @private
|
||||
|
|
@ -496,12 +619,13 @@
|
|||
|
||||
// Get the constraint point
|
||||
var constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY),
|
||||
localMouse = target.toLocalPoint(new fabric.Point(x, y), t.originX, t.originY);
|
||||
localMouse = target.toLocalPoint(new fabric.Point(x, y), t.originX, t.originY),
|
||||
dim = target._getTransformedDimensions();
|
||||
|
||||
this._setLocalMouse(localMouse, t);
|
||||
|
||||
// Actually scale the object
|
||||
this._setObjectScale(localMouse, t, lockScalingX, lockScalingY, by, lockScalingFlip);
|
||||
this._setObjectScale(localMouse, t, lockScalingX, lockScalingY, by, lockScalingFlip, dim);
|
||||
|
||||
// Make sure the constraints apply
|
||||
target.setPositionByOrigin(constraintPosition, t.originX, t.originY);
|
||||
|
|
@ -510,12 +634,11 @@
|
|||
/**
|
||||
* @private
|
||||
*/
|
||||
_setObjectScale: function(localMouse, transform, lockScalingX, lockScalingY, by, lockScalingFlip) {
|
||||
var target = transform.target, forbidScalingX = false, forbidScalingY = false,
|
||||
dim = target._getNonTransformedDimensions();
|
||||
_setObjectScale: function(localMouse, transform, lockScalingX, lockScalingY, by, lockScalingFlip, _dim) {
|
||||
var target = transform.target, forbidScalingX = false, forbidScalingY = false;
|
||||
|
||||
transform.newScaleX = localMouse.x / dim.x;
|
||||
transform.newScaleY = localMouse.y / dim.y;
|
||||
transform.newScaleX = localMouse.x * target.scaleX / _dim.x;
|
||||
transform.newScaleY = localMouse.y * target.scaleY / _dim.y;
|
||||
|
||||
if (lockScalingFlip && transform.newScaleX <= 0 && transform.newScaleX < target.scaleX) {
|
||||
forbidScalingX = true;
|
||||
|
|
@ -526,7 +649,7 @@
|
|||
}
|
||||
|
||||
if (by === 'equally' && !lockScalingX && !lockScalingY) {
|
||||
forbidScalingX || forbidScalingY || this._scaleObjectEqually(localMouse, target, transform);
|
||||
forbidScalingX || forbidScalingY || this._scaleObjectEqually(localMouse, target, transform, _dim);
|
||||
}
|
||||
else if (!by) {
|
||||
forbidScalingX || lockScalingX || target.set('scaleX', transform.newScaleX);
|
||||
|
|
@ -546,12 +669,11 @@
|
|||
/**
|
||||
* @private
|
||||
*/
|
||||
_scaleObjectEqually: function(localMouse, target, transform) {
|
||||
_scaleObjectEqually: function(localMouse, target, transform, _dim) {
|
||||
|
||||
var dist = localMouse.y + localMouse.x,
|
||||
dim = target._getNonTransformedDimensions(),
|
||||
lastDist = dim.y * transform.original.scaleY +
|
||||
dim.x * transform.original.scaleX;
|
||||
lastDist = _dim.y * transform.original.scaleY / target.scaleY +
|
||||
_dim.x * transform.original.scaleX / target.scaleX;
|
||||
|
||||
// We use transform.scaleX/Y instead of target.scaleX/Y
|
||||
// because the object may have a min scale and we'll loose the proportions
|
||||
|
|
@ -680,6 +802,8 @@
|
|||
_resetObjectTransform: function (target) {
|
||||
target.scaleX = 1;
|
||||
target.scaleY = 1;
|
||||
target.skewX = 0;
|
||||
target.skewY = 0;
|
||||
target.setAngle(0);
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -437,7 +437,7 @@
|
|||
target = this.getActiveGroup();
|
||||
}
|
||||
|
||||
if (target && target.selectable && !shouldGroup) {
|
||||
if (target && target.selectable && (target.__corner || !shouldGroup)) {
|
||||
this._beforeTransform(e, target);
|
||||
this._setupCurrentTransform(e, target);
|
||||
}
|
||||
|
|
@ -617,6 +617,14 @@
|
|||
this._scaleObject(x, y, 'y');
|
||||
this._fire('scaling', target, e);
|
||||
}
|
||||
else if (action === 'skewX') {
|
||||
this._skewObject(x, y, 'x');
|
||||
this._fire('skewing', target, e);
|
||||
}
|
||||
else if (action === 'skewY') {
|
||||
this._skewObject(x, y, 'y');
|
||||
this._fire('skewing', target, e);
|
||||
}
|
||||
else {
|
||||
this._translateObject(x, y);
|
||||
this._fire('moving', target, e);
|
||||
|
|
@ -637,14 +645,14 @@
|
|||
*/
|
||||
_beforeScaleTransform: function(e, transform) {
|
||||
if (transform.action === 'scale' || transform.action === 'scaleX' || transform.action === 'scaleY') {
|
||||
var centerTransform = this._shouldCenterTransform(e, transform.target);
|
||||
var centerTransform = this._shouldCenterTransform(transform.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);
|
||||
this._resetCurrentTransform();
|
||||
transform.reset = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -663,7 +671,7 @@
|
|||
else {
|
||||
// Switch from a normal resize to proportional
|
||||
if (!transform.reset && transform.currentAction === 'scale') {
|
||||
this._resetCurrentTransform(e, transform.target);
|
||||
this._resetCurrentTransform();
|
||||
}
|
||||
|
||||
transform.currentAction = 'scaleEqually';
|
||||
|
|
@ -693,7 +701,7 @@
|
|||
this.setCursor(target.hoverCursor || this.hoverCursor);
|
||||
}
|
||||
else {
|
||||
this._setCornerCursor(corner, target);
|
||||
this._setCornerCursor(corner, target, e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
@ -702,9 +710,9 @@
|
|||
/**
|
||||
* @private
|
||||
*/
|
||||
_setCornerCursor: function(corner, target) {
|
||||
_setCornerCursor: function(corner, target, e) {
|
||||
if (corner in cursorOffset) {
|
||||
this.setCursor(this._getRotatedCornerCursor(corner, target));
|
||||
this.setCursor(this._getRotatedCornerCursor(corner, target, e));
|
||||
}
|
||||
else if (corner === 'mtr' && target.hasRotatingPoint) {
|
||||
this.setCursor(this.rotationCursor);
|
||||
|
|
@ -718,13 +726,17 @@
|
|||
/**
|
||||
* @private
|
||||
*/
|
||||
_getRotatedCornerCursor: function(corner, target) {
|
||||
_getRotatedCornerCursor: function(corner, target, e) {
|
||||
var n = Math.round((target.getAngle() % 360) / 45);
|
||||
|
||||
if (n < 0) {
|
||||
n += 8; // full circle ahead
|
||||
}
|
||||
n += cursorOffset[corner];
|
||||
if (e.shiftKey && cursorOffset[corner] % 2 === 0) {
|
||||
//if we are holding shift and we are on a mx corner...
|
||||
n += 2;
|
||||
}
|
||||
// normalize n to be from 0 to 7
|
||||
n %= 8;
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
}
|
||||
var toFixed = fabric.util.toFixed,
|
||||
angle = this.getAngle(),
|
||||
skewX = (this.getSkewX() % 360),
|
||||
skewY = (this.getSkewY() % 360),
|
||||
vpt = !this.canvas || this.canvas.svgViewportTransformation ? this.getViewportTransform() : [1, 0, 0, 1, 0, 0],
|
||||
center = fabric.util.transformPoint(this.getCenterPoint(), vpt),
|
||||
|
||||
|
|
@ -81,6 +83,10 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
toFixed(this.scaleY * vpt[3], NUM_FRACTION_DIGITS) +
|
||||
')'),
|
||||
|
||||
skewXPart = skewX !== 0 ? ' skewX(' + toFixed(skewX, NUM_FRACTION_DIGITS) + ')' : '',
|
||||
|
||||
skewYPart = skewY !== 0 ? ' skewY(' + toFixed(skewY, NUM_FRACTION_DIGITS) + ')' : '',
|
||||
|
||||
addTranslateX = this.type === 'path-group' ? this.width * vpt[0] : 0,
|
||||
|
||||
flipXPart = this.flipX ? ' matrix(-1 0 0 1 ' + addTranslateX + ' 0) ' : '',
|
||||
|
|
@ -90,7 +96,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
flipYPart = this.flipY ? ' matrix(1 0 0 -1 0 ' + addTranslateY + ')' : '';
|
||||
|
||||
return [
|
||||
translatePart, anglePart, scalePart, flipXPart, flipYPart
|
||||
translatePart, anglePart, scalePart, flipXPart, flipYPart, skewXPart, skewYPart
|
||||
].join('');
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -192,23 +192,12 @@
|
|||
*/
|
||||
getBoundingRect: function() {
|
||||
this.oCoords || this.setCoords();
|
||||
|
||||
var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x],
|
||||
minX = fabric.util.array.min(xCoords),
|
||||
maxX = fabric.util.array.max(xCoords),
|
||||
width = Math.abs(minX - maxX),
|
||||
|
||||
yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y],
|
||||
minY = fabric.util.array.min(yCoords),
|
||||
maxY = fabric.util.array.max(yCoords),
|
||||
height = Math.abs(minY - maxY);
|
||||
|
||||
return {
|
||||
left: minX,
|
||||
top: minY,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
return fabric.util.makeBoundingBoxFromPoints([
|
||||
this.oCoords.tl,
|
||||
this.oCoords.tr,
|
||||
this.oCoords.br,
|
||||
this.oCoords.bl
|
||||
]);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -217,7 +206,7 @@
|
|||
*/
|
||||
getWidth: function() {
|
||||
//needs to be changed
|
||||
return this.width * this.scaleX;
|
||||
return this._getTransformedDimensions().x;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -226,7 +215,7 @@
|
|||
*/
|
||||
getHeight: function() {
|
||||
//needs to be changed
|
||||
return this.height * this.scaleY;
|
||||
return this._getTransformedDimensions().y;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -357,9 +346,12 @@
|
|||
return this;
|
||||
},
|
||||
|
||||
_calcDimensionsTransformMatrix: function() {
|
||||
// introduce skew matrix here later
|
||||
return [this.scaleX, 0, 0, this.scaleY, 0, 0];
|
||||
_calcDimensionsTransformMatrix: function(skewX, skewY) {
|
||||
var skewMatrixX = [1, 0, Math.tan(degreesToRadians(skewX)), 1, 0, 0],
|
||||
skewMatrixY = [1, Math.tan(degreesToRadians(skewY)), 0, 1, 0, 0],
|
||||
scaleMatrix = [this.scaleX, 0, 0, this.scaleY, 0, 0],
|
||||
m = fabric.util.multiplyTransformMatrices(scaleMatrix, skewMatrixX, true);
|
||||
return fabric.util.multiplyTransformMatrices(m, skewMatrixY, true);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -138,12 +138,39 @@
|
|||
/*
|
||||
* @private
|
||||
*/
|
||||
_getTransformedDimensions: function(dimensions) {
|
||||
if (!dimensions) {
|
||||
dimensions = this._getNonTransformedDimensions();
|
||||
_getTransformedDimensions: function(skewX, skewY) {
|
||||
if (typeof skewX === 'undefined') {
|
||||
skewX = this.skewX;
|
||||
}
|
||||
var transformMatrix = this._calcDimensionsTransformMatrix();
|
||||
return fabric.util.transformPoint(dimensions, transformMatrix, true);
|
||||
if (typeof skewY === 'undefined') {
|
||||
skewY = this.skewY;
|
||||
}
|
||||
var dimensions = this._getNonTransformedDimensions(),
|
||||
dimX = dimensions.x /2, dimY = dimensions.y / 2,
|
||||
points = [
|
||||
{
|
||||
x: -dimX,
|
||||
y: -dimY
|
||||
},
|
||||
{
|
||||
x: dimX,
|
||||
y: -dimY
|
||||
},
|
||||
{
|
||||
x: -dimX,
|
||||
y: dimY
|
||||
},
|
||||
{
|
||||
x: dimX,
|
||||
y: dimY
|
||||
}],
|
||||
i, transformMatrix = this._calcDimensionsTransformMatrix(skewX, skewY),
|
||||
bbox;
|
||||
for (i = 0; i < points.length; i++) {
|
||||
points[i] = fabric.util.transformPoint(points[i], transformMatrix);
|
||||
}
|
||||
bbox = fabric.util.makeBoundingBoxFromPoints(points);
|
||||
return { x: bbox.width, y: bbox.height };
|
||||
},
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
var setObjectScaleOverridden = fabric.Canvas.prototype._setObjectScale;
|
||||
|
||||
fabric.Canvas.prototype._setObjectScale = function(localMouse, transform,
|
||||
lockScalingX, lockScalingY, by, lockScalingFlip) {
|
||||
lockScalingX, lockScalingY, by, lockScalingFlip, _dim) {
|
||||
|
||||
var t = transform.target;
|
||||
if (t instanceof fabric.Textbox) {
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
}
|
||||
else {
|
||||
setObjectScaleOverridden.call(fabric.Canvas.prototype, localMouse, transform,
|
||||
lockScalingX, lockScalingY, by, lockScalingFlip);
|
||||
lockScalingX, lockScalingY, by, lockScalingFlip, _dim);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
* @fires rotating
|
||||
* @fires scaling
|
||||
* @fires moving
|
||||
* @fires skewing
|
||||
*
|
||||
* @fires mousedown
|
||||
* @fires mouseup
|
||||
|
|
@ -382,6 +383,20 @@
|
|||
*/
|
||||
angle: 0,
|
||||
|
||||
/**
|
||||
* Angle of skew on x axes of an object (in degrees)
|
||||
* @type Number
|
||||
* @default
|
||||
*/
|
||||
skewX: 0,
|
||||
|
||||
/**
|
||||
* Angle of skew on y axes of an object (in degrees)
|
||||
* @type Number
|
||||
* @default
|
||||
*/
|
||||
skewY: 0,
|
||||
|
||||
/**
|
||||
* Size of object's controlling corners (in pixels)
|
||||
* @type Number
|
||||
|
|
@ -662,6 +677,20 @@
|
|||
*/
|
||||
lockUniScaling: false,
|
||||
|
||||
/**
|
||||
* When `true`, object horizontal skewing is locked
|
||||
* @type Boolean
|
||||
* @default
|
||||
*/
|
||||
lockSkewingX: false,
|
||||
|
||||
/**
|
||||
* When `true`, object vertical skewing is locked
|
||||
* @type Boolean
|
||||
* @default
|
||||
*/
|
||||
lockSkewingY: false,
|
||||
|
||||
/**
|
||||
* When `true`, object cannot be flipped by scaling into negative values
|
||||
* @type Boolean
|
||||
|
|
@ -679,7 +708,7 @@
|
|||
'top left width height scaleX scaleY flipX flipY originX originY transformMatrix ' +
|
||||
'stroke strokeWidth strokeDashArray strokeLineCap strokeLineJoin strokeMiterLimit ' +
|
||||
'angle opacity fill fillRule globalCompositeOperation shadow clipTo visible backgroundColor ' +
|
||||
'alignX alignY meetOrSlice'
|
||||
'alignX alignY meetOrSlice skewX skewY'
|
||||
).split(' '),
|
||||
|
||||
/**
|
||||
|
|
@ -762,6 +791,8 @@
|
|||
this.scaleX * (this.flipX ? -1 : 1),
|
||||
this.scaleY * (this.flipY ? -1 : 1)
|
||||
);
|
||||
ctx.transform(1, 0, Math.tan(degreesToRadians(this.skewX)), 1, 0, 0);
|
||||
ctx.transform(1, Math.tan(degreesToRadians(this.skewY)), 0, 1, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -799,7 +830,9 @@
|
|||
backgroundColor: this.backgroundColor,
|
||||
fillRule: this.fillRule,
|
||||
globalCompositeOperation: this.globalCompositeOperation,
|
||||
transformMatrix: this.transformMatrix ? this.transformMatrix.concat() : this.transformMatrix
|
||||
transformMatrix: this.transformMatrix ? this.transformMatrix.concat() : this.transformMatrix,
|
||||
skewX: toFixed(this.skewX, NUM_FRACTION_DIGITS),
|
||||
skewY: toFixed(this.skewY, NUM_FRACTION_DIGITS)
|
||||
};
|
||||
|
||||
if (!this.includeDefaultValues) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
var sqrt = Math.sqrt,
|
||||
atan2 = Math.atan2,
|
||||
atan = Math.atan,
|
||||
pow = Math.pow,
|
||||
PiBy180 = Math.PI / 180;
|
||||
|
||||
/**
|
||||
|
|
@ -100,6 +102,29 @@
|
|||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns coordinates of points's bounding rectangle (left, top, width, height)
|
||||
* @param {Array} points 4 points array
|
||||
* @return {Object} Object with left, top, width, height properties
|
||||
*/
|
||||
makeBoundingBoxFromPoints: function(points) {
|
||||
var xPoints = [points[0].x, points[1].x, points[2].x, points[3].x],
|
||||
minX = fabric.util.array.min(xPoints),
|
||||
maxX = fabric.util.array.max(xPoints),
|
||||
width = Math.abs(minX - maxX),
|
||||
yPoints = [points[0].y, points[1].y, points[2].y, points[3].y],
|
||||
minY = fabric.util.array.min(yPoints),
|
||||
maxY = fabric.util.array.max(yPoints),
|
||||
height = Math.abs(minY - maxY);
|
||||
|
||||
return {
|
||||
left: minX,
|
||||
top: minY,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Invert transformation t
|
||||
* @static
|
||||
|
|
@ -457,20 +482,43 @@
|
|||
* @memberOf fabric.util
|
||||
* @param {Array} a First transformMatrix
|
||||
* @param {Array} b Second transformMatrix
|
||||
* @param {Boolean} is2x2 flag to multiply matrices as 2x2 matrices
|
||||
* @return {Array} The product of the two transform matrices
|
||||
*/
|
||||
multiplyTransformMatrices: function(a, b) {
|
||||
multiplyTransformMatrices: function(a, b, is2x2) {
|
||||
// Matrix multiply a * b
|
||||
return [
|
||||
a[0] * b[0] + a[2] * b[1],
|
||||
a[1] * b[0] + a[3] * b[1],
|
||||
a[0] * b[2] + a[2] * b[3],
|
||||
a[1] * b[2] + a[3] * b[3],
|
||||
a[0] * b[4] + a[2] * b[5] + a[4],
|
||||
a[1] * b[4] + a[3] * b[5] + a[5]
|
||||
is2x2 ? 0 : a[0] * b[4] + a[2] * b[5] + a[4],
|
||||
is2x2 ? 0 : a[1] * b[4] + a[3] * b[5] + a[5]
|
||||
];
|
||||
},
|
||||
|
||||
/**
|
||||
* Decompone standard 2x2 matrix into transform componentes
|
||||
* @static
|
||||
* @memberOf fabric.util
|
||||
* @param {Array} a transformMatrix
|
||||
* @return {Object} Components of transform
|
||||
*/
|
||||
qrDecompone: function(a) {
|
||||
var angle = atan(a[0] / a[1]),
|
||||
denom = pow(a[0]) + pow(a[1]),
|
||||
scaleX = sqrt(denom),
|
||||
scaleY = (a[0] * a[3] - a[2] * a [1]) / scaleX,
|
||||
skewX = atan((a[0] * a[2] + a[1] * a [3]) / denom);
|
||||
return {
|
||||
angle: angle / PiBy180,
|
||||
scaleX: scaleX,
|
||||
scaleY: scaleY,
|
||||
skewX: skewX / PiBy180,
|
||||
skewY: 0
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns string representation of function body
|
||||
* @param {Function} fn Function to get body of
|
||||
|
|
|
|||
|
|
@ -41,12 +41,12 @@
|
|||
var PATH_DATALESS_JSON = '{"objects":[{"type":"path","originX":"left","originY":"top","left":100,"top":100,"width":200,"height":200,"fill":"rgb(0,0,0)",'+
|
||||
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+
|
||||
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
|
||||
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"path":"http://example.com/","pathOffset":{"x":200,"y":200}}],"background":""}';
|
||||
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"path":"http://example.com/","pathOffset":{"x":200,"y":200}}],"background":""}';
|
||||
|
||||
var RECT_JSON = '{"objects":[{"type":"rect","originX":"left","originY":"top","left":0,"top":0,"width":10,"height":10,"fill":"rgb(0,0,0)",'+
|
||||
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
|
||||
'"shadow":null,'+
|
||||
'"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
|
||||
'"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
|
||||
|
||||
var el = fabric.document.createElement('canvas');
|
||||
el.width = 600; el.height = 600;
|
||||
|
|
|
|||
|
|
@ -28,17 +28,17 @@
|
|||
var PATH_DATALESS_JSON = '{"objects":[{"type":"path","originX":"left","originY":"top","left":100,"top":100,"width":200,"height":200,"fill":"rgb(0,0,0)",'+
|
||||
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+
|
||||
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
|
||||
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"path":"http://example.com/","pathOffset":{"x":200,"y":200}}],"background":""}';
|
||||
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"path":"http://example.com/","pathOffset":{"x":200,"y":200}}],"background":""}';
|
||||
|
||||
var RECT_JSON = '{"objects":[{"type":"rect","originX":"left","originY":"top","left":0,"top":0,"width":10,"height":10,"fill":"rgb(0,0,0)",'+
|
||||
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+
|
||||
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
|
||||
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
|
||||
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
|
||||
|
||||
var RECT_JSON_WITH_PADDING = '{"objects":[{"type":"rect","originX":"left","originY":"top","left":0,"top":0,"width":10,"height":20,"fill":"rgb(0,0,0)",'+
|
||||
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+
|
||||
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
|
||||
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"padding":123,"foo":"bar","rx":0,"ry":0}],"background":""}';
|
||||
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"padding":123,"foo":"bar","rx":0,"ry":0}],"background":""}';
|
||||
|
||||
function getAbsolutePath(path) {
|
||||
var isAbsolute = /^https?:/.test(path);
|
||||
|
|
@ -85,6 +85,8 @@
|
|||
'globalCompositeOperation': 'source-over',
|
||||
'transformMatrix': null,
|
||||
'crossOrigin': '',
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'alignX': 'none',
|
||||
'alignY': 'none',
|
||||
'meetOrSlice': 'meet'
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
});
|
||||
|
||||
test('setRadius', function() {
|
||||
var circle = new fabric.Circle({ radius: 10 });
|
||||
var circle = new fabric.Circle({radius: 10, strokeWidth: 0});
|
||||
|
||||
ok(typeof circle.setRadius == 'function');
|
||||
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
});
|
||||
|
||||
test('set radius', function() {
|
||||
var circle = new fabric.Circle();
|
||||
var circle = new fabric.Circle({strokeWidth: 0});
|
||||
|
||||
circle.set('radius', 20);
|
||||
|
||||
|
|
@ -108,6 +108,8 @@
|
|||
'radius': 0,
|
||||
'startAngle': 0,
|
||||
'endAngle': 2 * Math.PI,
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'transformMatrix': null
|
||||
};
|
||||
ok(typeof circle.toObject == 'function');
|
||||
|
|
@ -218,7 +220,7 @@
|
|||
});
|
||||
|
||||
test('cloning and radius, width, height', function() {
|
||||
var circle = new fabric.Circle({ radius: 10 });
|
||||
var circle = new fabric.Circle({ radius: 10, strokeWidth: 0});
|
||||
circle.scale(2);
|
||||
|
||||
var clone = circle.clone();
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@
|
|||
'flipX': false,
|
||||
'flipY': false,
|
||||
'opacity': 1,
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'rx': 0,
|
||||
'ry': 0,
|
||||
'shadow': null,
|
||||
|
|
|
|||
|
|
@ -191,6 +191,8 @@
|
|||
'fillRule': 'nonzero',
|
||||
'globalCompositeOperation': 'source-over',
|
||||
'transformMatrix': null,
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'objects': clone.objects
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@
|
|||
'filters': [],
|
||||
'fillRule': 'nonzero',
|
||||
'globalCompositeOperation': 'source-over',
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'transformMatrix': null,
|
||||
'crossOrigin': '',
|
||||
'alignX': 'none',
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@
|
|||
'textBackgroundColor': '',
|
||||
'fillRule': 'nonzero',
|
||||
'globalCompositeOperation': 'source-over',
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'transformMatrix': null,
|
||||
styles: { }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
'backgroundColor': '',
|
||||
'fillRule': 'nonzero',
|
||||
'globalCompositeOperation': 'source-over',
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'transformMatrix': null
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -154,13 +154,13 @@
|
|||
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+
|
||||
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
|
||||
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over",'+
|
||||
'"transformMatrix":null}';
|
||||
'"transformMatrix":null,"skewX":0,"skewY":0}';
|
||||
|
||||
var augmentedJSON = '{"type":"object","originX":"left","originY":"top","left":0,"top":0,"width":122,"height":0,"fill":"rgb(0,0,0)",'+
|
||||
'"stroke":null,"strokeWidth":1,"strokeDashArray":[5,2],"strokeLineCap":"round","strokeLineJoin":"bevil","strokeMiterLimit":5,'+
|
||||
'"scaleX":1.3,"scaleY":1,"angle":0,"flipX":false,"flipY":true,"opacity":0.88,'+
|
||||
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over",'+
|
||||
'"transformMatrix":null}';
|
||||
'"transformMatrix":null,"skewX":0,"skewY":0}';
|
||||
|
||||
var cObj = new fabric.Object();
|
||||
ok(typeof cObj.toJSON == 'function');
|
||||
|
|
@ -206,6 +206,8 @@
|
|||
'clipTo': null,
|
||||
'fillRule': 'nonzero',
|
||||
'globalCompositeOperation': 'source-over',
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'transformMatrix': null
|
||||
};
|
||||
|
||||
|
|
@ -236,7 +238,9 @@
|
|||
'clipTo': null,
|
||||
'fillRule': 'nonzero',
|
||||
'globalCompositeOperation': 'source-over',
|
||||
'transformMatrix': null
|
||||
'transformMatrix': null,
|
||||
'skewX': 0,
|
||||
'skewY': 0
|
||||
};
|
||||
|
||||
var cObj = new fabric.Object();
|
||||
|
|
@ -408,17 +412,17 @@ test('getBoundingRectWithStroke', function() {
|
|||
});
|
||||
|
||||
test('getWidth', function() {
|
||||
var cObj = new fabric.Object({ strokeWidth: 0 });
|
||||
var cObj = new fabric.Object();
|
||||
ok(typeof cObj.getWidth == 'function');
|
||||
equal(cObj.getWidth(), 0);
|
||||
equal(cObj.getWidth(), 0 + cObj.strokeWidth);
|
||||
cObj.set('width', 123);
|
||||
equal(cObj.getWidth(), 123);
|
||||
equal(cObj.getWidth(), 123 + cObj.strokeWidth);
|
||||
cObj.set('scaleX', 2);
|
||||
equal(cObj.getWidth(), 246);
|
||||
equal(cObj.getWidth(), 246 + cObj.strokeWidth * 2);
|
||||
});
|
||||
|
||||
test('getHeight', function() {
|
||||
var cObj = new fabric.Object();
|
||||
var cObj = new fabric.Object({strokeWidth: 0});
|
||||
ok(typeof cObj.getHeight == 'function');
|
||||
equal(cObj.getHeight(), 0);
|
||||
cObj.set('height', 123);
|
||||
|
|
|
|||
|
|
@ -237,6 +237,7 @@
|
|||
var rect = new fabric.Rect(rectOptions),
|
||||
p;
|
||||
|
||||
rect.strokeWidth = 0;
|
||||
rect.originX = 'left';
|
||||
rect.originY = 'top';
|
||||
p = rect.adjustPosition('left');
|
||||
|
|
@ -294,7 +295,7 @@
|
|||
p;
|
||||
|
||||
rect.angle = 35;
|
||||
|
||||
rect.strokeWidth = 0;
|
||||
rect.originX = 'left';
|
||||
rect.originY = 'top';
|
||||
p = rect.adjustPosition('left');
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
'clipTo': null,
|
||||
'fillRule': 'nonzero',
|
||||
'globalCompositeOperation': 'source-over',
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'transformMatrix': null
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
'fillRule': 'nonzero',
|
||||
'globalCompositeOperation': 'source-over',
|
||||
'transformMatrix': null,
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'paths': getPathObjects()
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
'clipTo': null,
|
||||
'fillRule': 'nonzero',
|
||||
'globalCompositeOperation': 'source-over',
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'transformMatrix': null
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
'clipTo': null,
|
||||
'fillRule': 'nonzero',
|
||||
'globalCompositeOperation': 'source-over',
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'transformMatrix': null
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@
|
|||
'fillRule': 'nonzero',
|
||||
'globalCompositeOperation': 'source-over',
|
||||
'transformMatrix': null,
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'rx': 0,
|
||||
'ry': 0,
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
};
|
||||
|
||||
QUnit.module('fabric.Rect');
|
||||
|
|
@ -104,7 +104,6 @@
|
|||
elRectWithAttrs.setAttribute('stroke-linecap', 'round');
|
||||
elRectWithAttrs.setAttribute('stroke-linejoin', 'bevil');
|
||||
elRectWithAttrs.setAttribute('stroke-miterlimit', 5);
|
||||
elRectWithAttrs.setAttribute('skewX', 30);
|
||||
//elRectWithAttrs.setAttribute('transform', 'translate(-10,-20) scale(2) rotate(45) translate(5,10)');
|
||||
|
||||
var rectWithAttrs = fabric.Rect.fromElement(elRectWithAttrs);
|
||||
|
|
@ -123,7 +122,6 @@
|
|||
strokeLineCap: 'round',
|
||||
strokeLineJoin: 'bevil',
|
||||
strokeMiterLimit: 5,
|
||||
skewX: 30,
|
||||
rx: 11,
|
||||
ry: 12
|
||||
});
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@
|
|||
'textBackgroundColor': '',
|
||||
'fillRule': 'nonzero',
|
||||
'globalCompositeOperation': 'source-over',
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'transformMatrix': null
|
||||
};
|
||||
|
||||
|
|
@ -154,8 +156,8 @@
|
|||
// text.width = CHAR_WIDTH;
|
||||
|
||||
var expectedObject = fabric.util.object.extend(fabric.util.object.clone(REFERENCE_TEXT_OBJECT), {
|
||||
left: 4,
|
||||
top: -3.61,
|
||||
left: 4.5,
|
||||
top: -4.11,
|
||||
width: 8,
|
||||
height: 20.97,
|
||||
fontSize: 16,
|
||||
|
|
@ -196,7 +198,7 @@
|
|||
var expectedObject = fabric.util.object.extend(fabric.util.object.clone(REFERENCE_TEXT_OBJECT), {
|
||||
/* left varies slightly due to node-canvas rendering */
|
||||
left: fabric.util.toFixed(textWithAttrs.left + '', 2),
|
||||
top: -7.72,
|
||||
top: -9.22,
|
||||
width: CHAR_WIDTH,
|
||||
height: 161.23,
|
||||
fill: 'rgb(255,255,255)',
|
||||
|
|
|
|||
Loading…
Reference in a new issue