mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-04-08 00:10:59 +00:00
commit
4e64cf499a
18 changed files with 372 additions and 205 deletions
|
|
@ -26,13 +26,18 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
|
|||
*/
|
||||
drawDot: function(pointer) {
|
||||
var point = this.addPoint(pointer),
|
||||
ctx = this.canvas.contextTop;
|
||||
ctx = this.canvas.contextTop,
|
||||
v = this.canvas.viewportTransform;
|
||||
ctx.save();
|
||||
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
|
||||
ctx.fillStyle = point.fill;
|
||||
ctx.beginPath();
|
||||
ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -78,6 +83,7 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
|
|||
circles.push(circle);
|
||||
}
|
||||
var group = new fabric.Group(circles, { originX: 'center', originY: 'center' });
|
||||
group.canvas = this.canvas;
|
||||
|
||||
this.canvas.add(group);
|
||||
this.canvas.fire('path:created', { path: group });
|
||||
|
|
|
|||
|
|
@ -104,6 +104,9 @@
|
|||
*/
|
||||
_render: function() {
|
||||
var ctx = this.canvas.contextTop;
|
||||
var v = this.canvas.viewportTransform;
|
||||
ctx.save();
|
||||
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
ctx.beginPath();
|
||||
|
||||
var p1 = this._points[0],
|
||||
|
|
@ -133,6 +136,7 @@
|
|||
// the bezier control point
|
||||
ctx.lineTo(p1.x, p1.y);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -112,6 +112,8 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
|
|||
}
|
||||
|
||||
var group = new fabric.Group(rects, { originX: 'center', originY: 'center' });
|
||||
group.canvas = this.canvas;
|
||||
|
||||
this.canvas.add(group);
|
||||
this.canvas.fire('path:created', { path: group });
|
||||
|
||||
|
|
@ -146,7 +148,10 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
|
|||
render: function() {
|
||||
var ctx = this.canvas.contextTop;
|
||||
ctx.fillStyle = this.color;
|
||||
|
||||
var v = this.canvas.viewportTransform;
|
||||
ctx.save();
|
||||
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
|
||||
for (var i = 0, len = this.sprayChunkPoints.length; i < len; i++) {
|
||||
var point = this.sprayChunkPoints[i];
|
||||
|
|
@ -180,8 +185,9 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
|
|||
else {
|
||||
width = this.dotWidth;
|
||||
}
|
||||
|
||||
var point = { x: x, y: y, width: width };
|
||||
|
||||
var point = new fabric.Point(x, y);
|
||||
point.width = width;
|
||||
|
||||
if (this.randomOpacity) {
|
||||
point.opacity = fabric.util.getRandomInt(0, 100) / 100;
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@
|
|||
* @return {Boolean} true if point is contained within an area of given object
|
||||
*/
|
||||
containsPoint: function (e, target) {
|
||||
var pointer = this.getPointer(e),
|
||||
var pointer = this.getPointer(e, true),
|
||||
xy = this._normalizePointer(target, pointer);
|
||||
|
||||
// http://www.geog.ubc.ca/courses/klink/gis.notes/ncgia/u32.html
|
||||
|
|
@ -268,11 +268,14 @@
|
|||
isObjectInGroup = (
|
||||
activeGroup &&
|
||||
object.type !== 'group' &&
|
||||
activeGroup.contains(object));
|
||||
activeGroup.contains(object)),
|
||||
lt;
|
||||
|
||||
if (isObjectInGroup) {
|
||||
x -= activeGroup.left;
|
||||
y -= activeGroup.top;
|
||||
lt = new fabric.Point(activeGroup.left, activeGroup.top);
|
||||
lt = fabric.util.transformPoint(lt, this.viewportTransform, true);
|
||||
x -= lt.x;
|
||||
y -= lt.y;
|
||||
}
|
||||
return { x: x, y: y };
|
||||
},
|
||||
|
|
@ -403,7 +406,7 @@
|
|||
if (!target) return;
|
||||
|
||||
var pointer = this.getPointer(e),
|
||||
corner = target._findTargetCorner(pointer),
|
||||
corner = target._findTargetCorner(this.getPointer(e, true)),
|
||||
action = this._getActionFromCorner(target, corner),
|
||||
origin = this._getOriginFromCorner(target, corner);
|
||||
|
||||
|
|
@ -708,7 +711,7 @@
|
|||
this.lastRenderedObjectWithControlsAboveOverlay &&
|
||||
this.lastRenderedObjectWithControlsAboveOverlay.visible &&
|
||||
this.containsPoint(e, this.lastRenderedObjectWithControlsAboveOverlay) &&
|
||||
this.lastRenderedObjectWithControlsAboveOverlay._findTargetCorner(this.getPointer(e)));
|
||||
this.lastRenderedObjectWithControlsAboveOverlay._findTargetCorner(this.getPointer(e, true)));
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -731,6 +734,7 @@
|
|||
|
||||
var target = this._searchPossibleTargets(e);
|
||||
this._fireOverOutEvents(target);
|
||||
|
||||
return target;
|
||||
},
|
||||
|
||||
|
|
@ -783,7 +787,7 @@
|
|||
|
||||
// Cache all targets where their bounding box contains point.
|
||||
var target,
|
||||
pointer = this.getPointer(e),
|
||||
pointer = this.getPointer(e, true),
|
||||
i = this._objects.length;
|
||||
|
||||
while (i--) {
|
||||
|
|
@ -802,24 +806,36 @@
|
|||
* @param {Event} e
|
||||
* @return {Object} object with "x" and "y" number values
|
||||
*/
|
||||
getPointer: function (e) {
|
||||
var pointer = getPointer(e, this.upperCanvasEl),
|
||||
bounds = this.upperCanvasEl.getBoundingClientRect(),
|
||||
getPointer: function (e, ignoreZoom, upperCanvasEl) {
|
||||
if (!upperCanvasEl) {
|
||||
upperCanvasEl = this.upperCanvasEl;
|
||||
}
|
||||
var pointer = getPointer(e, upperCanvasEl),
|
||||
bounds = upperCanvasEl.getBoundingClientRect(),
|
||||
cssScale;
|
||||
|
||||
pointer.x = pointer.x - this._offset.left;
|
||||
pointer.y = pointer.y - this._offset.top;
|
||||
if (!ignoreZoom) {
|
||||
pointer = fabric.util.transformPoint(
|
||||
pointer,
|
||||
fabric.util.invertTransform(this.viewportTransform)
|
||||
);
|
||||
}
|
||||
|
||||
if (bounds.width === 0 || bounds.height === 0) {
|
||||
// If bounds are not available (i.e. not visible), do not apply scale.
|
||||
cssScale = { width: 1, height: 1 };
|
||||
}
|
||||
else {
|
||||
cssScale = {
|
||||
width: this.upperCanvasEl.width / bounds.width,
|
||||
height: this.upperCanvasEl.height / bounds.height
|
||||
width: upperCanvasEl.width / bounds.width,
|
||||
height: upperCanvasEl.height / bounds.height
|
||||
};
|
||||
}
|
||||
return {
|
||||
x: (pointer.x - this._offset.left) * cssScale.width,
|
||||
y: (pointer.y - this._offset.top) * cssScale.height
|
||||
x: pointer.x * cssScale.width,
|
||||
y: pointer.y * cssScale.height
|
||||
};
|
||||
},
|
||||
|
||||
|
|
@ -978,7 +994,6 @@
|
|||
_setActiveGroup: function(group) {
|
||||
this._activeGroup = group;
|
||||
if (group) {
|
||||
group.canvas = this;
|
||||
group.set('active', true);
|
||||
}
|
||||
},
|
||||
|
|
@ -1077,7 +1092,7 @@
|
|||
* @private
|
||||
*/
|
||||
_drawGroupControls: function(ctx, activeGroup) {
|
||||
this._drawControls(ctx, activeGroup, 'Group');
|
||||
activeGroup._renderControls(ctx);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -1086,19 +1101,9 @@
|
|||
_drawObjectsControls: function(ctx) {
|
||||
for (var i = 0, len = this._objects.length; i < len; ++i) {
|
||||
if (!this._objects[i] || !this._objects[i].active) continue;
|
||||
this._drawControls(ctx, this._objects[i], 'Object');
|
||||
this._objects[i]._renderControls(ctx);
|
||||
this.lastRenderedObjectWithControlsAboveOverlay = this._objects[i];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_drawControls: function(ctx, object, klass) {
|
||||
ctx.save();
|
||||
fabric[klass].prototype.transform.call(object, ctx);
|
||||
object.drawBorders(ctx).drawControls(ctx);
|
||||
ctx.restore();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -334,7 +334,9 @@
|
|||
if (this.clipTo) {
|
||||
fabric.util.clipContext(this, this.contextTop);
|
||||
}
|
||||
this.freeDrawingBrush.onMouseDown(this.getPointer(e));
|
||||
var ivt = fabric.util.invertTransform(this.viewportTransform);
|
||||
var pointer = fabric.util.transformPoint(this.getPointer(e, true), ivt);
|
||||
this.freeDrawingBrush.onMouseDown(pointer);
|
||||
this.fire('mouse:down', { e: e });
|
||||
},
|
||||
|
||||
|
|
@ -344,7 +346,8 @@
|
|||
*/
|
||||
_onMouseMoveInDrawingMode: function(e) {
|
||||
if (this._isCurrentlyDrawing) {
|
||||
var pointer = this.getPointer(e);
|
||||
var ivt = fabric.util.invertTransform(this.viewportTransform),
|
||||
pointer = fabric.util.transformPoint(this.getPointer(e, true), ivt);
|
||||
this.freeDrawingBrush.onMouseMove(pointer);
|
||||
}
|
||||
this.upperCanvasEl.style.cursor = this.freeDrawingCursor;
|
||||
|
|
@ -387,7 +390,7 @@
|
|||
if (this._currentTransform) return;
|
||||
|
||||
var target = this.findTarget(e),
|
||||
pointer = this.getPointer(e);
|
||||
pointer = this.getPointer(e, true);
|
||||
|
||||
// save pointer for check in __onMouseUp event
|
||||
this._previousPointer = pointer;
|
||||
|
|
@ -514,7 +517,7 @@
|
|||
|
||||
// We initially clicked in an empty area, so we draw a box for multiple selection
|
||||
if (groupSelector) {
|
||||
pointer = this.getPointer(e);
|
||||
pointer = this.getPointer(e, true);
|
||||
|
||||
groupSelector.left = pointer.x - groupSelector.ex;
|
||||
groupSelector.top = pointer.y - groupSelector.ey;
|
||||
|
|
@ -545,7 +548,6 @@
|
|||
* @param {Event} e Event fired on mousemove
|
||||
*/
|
||||
_transformObject: function(e) {
|
||||
|
||||
var pointer = this.getPointer(e),
|
||||
transform = this._currentTransform;
|
||||
|
||||
|
|
@ -655,7 +657,7 @@
|
|||
// only show proper corner when group selection is not active
|
||||
corner = target._findTargetCorner
|
||||
&& (!activeGroup || !activeGroup.contains(target))
|
||||
&& target._findTargetCorner(this.getPointer(e));
|
||||
&& target._findTargetCorner(this.getPointer(e, true));
|
||||
|
||||
if (!corner) {
|
||||
style.cursor = target.hoverCursor || this.hoverCursor;
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@
|
|||
if (this._activeObject && target !== this._activeObject) {
|
||||
|
||||
var group = this._createGroup(target);
|
||||
group.addWithUpdate();
|
||||
|
||||
this.setActiveGroup(group);
|
||||
this._activeObject = null;
|
||||
|
|
@ -107,7 +108,8 @@
|
|||
|
||||
return new fabric.Group(groupObjects, {
|
||||
originX: 'center',
|
||||
originY: 'center'
|
||||
originY: 'center',
|
||||
canvas: this
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -126,8 +128,10 @@
|
|||
else if (group.length > 1) {
|
||||
group = new fabric.Group(group.reverse(), {
|
||||
originX: 'center',
|
||||
originY: 'center'
|
||||
originY: 'center',
|
||||
canvas: this
|
||||
});
|
||||
group.addWithUpdate();
|
||||
this.setActiveGroup(group, e);
|
||||
group.saveCoords();
|
||||
this.fire('selection:created', { target: group });
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
getSvgTransform: function() {
|
||||
var toFixed = fabric.util.toFixed,
|
||||
angle = this.getAngle(),
|
||||
center = this.getCenterPoint(),
|
||||
vpt = this.getViewportTransform(),
|
||||
center = fabric.util.transformPoint(this.getCenterPoint(), vpt),
|
||||
|
||||
NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
|
||||
|
||||
|
|
@ -60,12 +61,12 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
? (' rotate(' + toFixed(angle, NUM_FRACTION_DIGITS) + ')')
|
||||
: '',
|
||||
|
||||
scalePart = (this.scaleX === 1 && this.scaleY === 1)
|
||||
scalePart = (this.scaleX === 1 && this.scaleY === 1 && vpt[0] === 1 && vpt[3] === 1)
|
||||
? '' :
|
||||
(' scale(' +
|
||||
toFixed(this.scaleX, NUM_FRACTION_DIGITS) +
|
||||
toFixed(this.scaleX * vpt[0], NUM_FRACTION_DIGITS) +
|
||||
' ' +
|
||||
toFixed(this.scaleY, NUM_FRACTION_DIGITS) +
|
||||
toFixed(this.scaleY * vpt[3], NUM_FRACTION_DIGITS) +
|
||||
')'),
|
||||
|
||||
flipXPart = this.flipX ? 'matrix(-1 0 0 1 0 0) ' : '',
|
||||
|
|
|
|||
|
|
@ -304,11 +304,15 @@
|
|||
setCoords: function() {
|
||||
|
||||
var strokeWidth = this.strokeWidth > 1 ? this.strokeWidth : 0,
|
||||
padding = this.padding,
|
||||
theta = degreesToRadians(this.angle);
|
||||
theta = degreesToRadians(this.angle),
|
||||
vpt = this.getViewportTransform();
|
||||
|
||||
this.currentWidth = (this.width + strokeWidth) * this.scaleX + padding * 2;
|
||||
this.currentHeight = (this.height + strokeWidth) * this.scaleY + padding * 2;
|
||||
var f = function (p) {
|
||||
return fabric.util.transformPoint(p, vpt);
|
||||
};
|
||||
|
||||
this.currentWidth = (this.width + strokeWidth) * this.scaleX;
|
||||
this.currentHeight = (this.height + strokeWidth) * this.scaleY;
|
||||
|
||||
// If width is negative, make postive. Fixes path selection issue
|
||||
if (this.currentWidth < 0) {
|
||||
|
|
@ -326,45 +330,34 @@
|
|||
offsetY = Math.sin(_angle + theta) * _hypotenuse,
|
||||
sinTh = Math.sin(theta),
|
||||
cosTh = Math.cos(theta),
|
||||
|
||||
coords = this.getCenterPoint(),
|
||||
wh = new fabric.Point(this.currentWidth, this.currentHeight),
|
||||
_tl = new fabric.Point(coords.x - offsetX, coords.y - offsetY),
|
||||
_tr = new fabric.Point(_tl.x + (wh.x * cosTh), _tl.y + (wh.x * sinTh)),
|
||||
_bl = new fabric.Point(_tl.x - (wh.y * sinTh), _tl.y + (wh.y * cosTh)),
|
||||
_mt = new fabric.Point(_tl.x + (wh.x/2 * cosTh), _tl.y + (wh.x/2 * sinTh)),
|
||||
tl = f(_tl),
|
||||
tr = f(_tr),
|
||||
br = f(new fabric.Point(_tr.x - (wh.y * sinTh), _tr.y + (wh.y * cosTh))),
|
||||
bl = f(_bl),
|
||||
ml = f(new fabric.Point(_tl.x - (wh.y/2 * sinTh), _tl.y + (wh.y/2 * cosTh))),
|
||||
mt = f(_mt),
|
||||
mr = f(new fabric.Point(_tr.x - (wh.y/2 * sinTh), _tr.y + (wh.y/2 * cosTh))),
|
||||
mb = f(new fabric.Point(_bl.x + (wh.x/2 * cosTh), _bl.y + (wh.x/2 * sinTh))),
|
||||
mtr = f(new fabric.Point(_mt.x, _mt.y));
|
||||
|
||||
tl = {
|
||||
x: coords.x - offsetX,
|
||||
y: coords.y - offsetY
|
||||
},
|
||||
tr = {
|
||||
x: tl.x + (this.currentWidth * cosTh),
|
||||
y: tl.y + (this.currentWidth * sinTh)
|
||||
},
|
||||
br = {
|
||||
x: tr.x - (this.currentHeight * sinTh),
|
||||
y: tr.y + (this.currentHeight * cosTh)
|
||||
},
|
||||
bl = {
|
||||
x: tl.x - (this.currentHeight * sinTh),
|
||||
y: tl.y + (this.currentHeight * cosTh)
|
||||
},
|
||||
ml = {
|
||||
x: tl.x - (this.currentHeight/2 * sinTh),
|
||||
y: tl.y + (this.currentHeight/2 * cosTh)
|
||||
},
|
||||
mt = {
|
||||
x: tl.x + (this.currentWidth/2 * cosTh),
|
||||
y: tl.y + (this.currentWidth/2 * sinTh)
|
||||
},
|
||||
mr = {
|
||||
x: tr.x - (this.currentHeight/2 * sinTh),
|
||||
y: tr.y + (this.currentHeight/2 * cosTh)
|
||||
},
|
||||
mb = {
|
||||
x: bl.x + (this.currentWidth/2 * cosTh),
|
||||
y: bl.y + (this.currentWidth/2 * sinTh)
|
||||
},
|
||||
mtr = {
|
||||
x: mt.x,
|
||||
y: mt.y
|
||||
};
|
||||
// padding
|
||||
var padX = Math.cos(_angle + theta) * this.padding * Math.sqrt(2),
|
||||
padY = Math.sin(_angle + theta) * this.padding * Math.sqrt(2);
|
||||
tl = tl.add(new fabric.Point(-padX, -padY));
|
||||
tr = tr.add(new fabric.Point(padY, -padX));
|
||||
br = br.add(new fabric.Point(padX, padY));
|
||||
bl = bl.add(new fabric.Point(-padY, padX));
|
||||
ml = ml.add(new fabric.Point((-padX - padY) / 2, (-padY + padX) / 2));
|
||||
mt = mt.add(new fabric.Point((padY - padX) / 2, -(padY + padX) / 2));
|
||||
mr = mr.add(new fabric.Point((padY + padX) / 2, (padY - padX) / 2));
|
||||
mb = mb.add(new fabric.Point((padX - padY) / 2, (padX + padY) / 2));
|
||||
mtr = mtr.add(new fabric.Point((padY - padX) / 2, -(padY + padX) / 2));
|
||||
|
||||
// debugging
|
||||
|
||||
|
|
|
|||
|
|
@ -277,25 +277,32 @@
|
|||
scaleY = 1 / this._constrainScale(this.scaleY);
|
||||
|
||||
ctx.lineWidth = 1 / this.borderScaleFactor;
|
||||
|
||||
ctx.scale(scaleX, scaleY);
|
||||
|
||||
var w = this.getWidth(),
|
||||
h = this.getHeight();
|
||||
|
||||
var vpt = this.getViewportTransform(),
|
||||
wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), vpt, true),
|
||||
sxy = fabric.util.transformPoint(new fabric.Point(scaleX, scaleY), vpt, true),
|
||||
w = wh.x,
|
||||
h = wh.y,
|
||||
sx= sxy.x,
|
||||
sy= sxy.y;
|
||||
if (this.group) {
|
||||
w = w * this.group.scaleX;
|
||||
h = h * this.group.scaleY;
|
||||
}
|
||||
|
||||
ctx.strokeRect(
|
||||
~~(-(w / 2) - padding - strokeWidth / 2 * this.scaleX) - 0.5, // offset needed to make lines look sharper
|
||||
~~(-(h / 2) - padding - strokeWidth / 2 * this.scaleY) - 0.5,
|
||||
~~(w + padding2 + strokeWidth * this.scaleX) + 1, // double offset needed to make lines look sharper
|
||||
~~(h + padding2 + strokeWidth * this.scaleY) + 1
|
||||
~~(-(w / 2) - padding - strokeWidth / 2 * sx) - 0.5, // offset needed to make lines look sharper
|
||||
~~(-(h / 2) - padding - strokeWidth / 2 * sy) - 0.5,
|
||||
~~(w + padding2 + strokeWidth * sx) + 1, // double offset needed to make lines look sharper
|
||||
~~(h + padding2 + strokeWidth * sy) + 1
|
||||
);
|
||||
|
||||
if (this.hasRotatingPoint && this.isControlVisible('mtr') && !this.get('lockRotation') && this.hasControls) {
|
||||
|
||||
var rotateHeight = (
|
||||
this.flipY
|
||||
? h + (strokeWidth * this.scaleY) + (padding * 2)
|
||||
: -h - (strokeWidth * this.scaleY) - (padding * 2)
|
||||
? h + (strokeWidth * sx) + (padding * 2)
|
||||
: -h - (strokeWidth * sy) - (padding * 2)
|
||||
) / 2;
|
||||
|
||||
ctx.beginPath();
|
||||
|
|
@ -311,7 +318,7 @@
|
|||
|
||||
/**
|
||||
* Draws corners of an object's bounding box.
|
||||
* Requires public properties: width, height, scaleX, scaleY
|
||||
* Requires public properties: width, height
|
||||
* Requires public options: cornerSize, padding
|
||||
* @param {CanvasRenderingContext2D} ctx Context to draw on
|
||||
* @return {fabric.Object} thisArg
|
||||
|
|
@ -323,75 +330,73 @@
|
|||
var size = this.cornerSize,
|
||||
size2 = size / 2,
|
||||
strokeWidth2 = ~~(this.strokeWidth / 2), // half strokeWidth rounded down
|
||||
left = -(this.width / 2),
|
||||
top = -(this.height / 2),
|
||||
paddingX = this.padding / this.scaleX,
|
||||
paddingY = this.padding / this.scaleY,
|
||||
scaleOffsetY = size2 / this.scaleY,
|
||||
scaleOffsetX = size2 / this.scaleX,
|
||||
scaleOffsetSizeX = (size2 - size) / this.scaleX,
|
||||
scaleOffsetSizeY = (size2 - size) / this.scaleY,
|
||||
height = this.height,
|
||||
width = this.width,
|
||||
wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), this.getViewportTransform(), true),
|
||||
width = wh.x,
|
||||
height = wh.y,
|
||||
left = -(width / 2),
|
||||
top = -(height / 2),
|
||||
padding = this.padding,
|
||||
scaleOffset = size2,
|
||||
scaleOffsetSize = size2 - size,
|
||||
methodName = this.transparentCorners ? 'strokeRect' : 'fillRect';
|
||||
|
||||
ctx.save();
|
||||
|
||||
ctx.lineWidth = 1 / Math.max(this.scaleX, this.scaleY);
|
||||
ctx.lineWidth = 1;
|
||||
|
||||
ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
|
||||
ctx.strokeStyle = ctx.fillStyle = this.cornerColor;
|
||||
|
||||
// top-left
|
||||
this._drawControl('tl', ctx, methodName,
|
||||
left - scaleOffsetX - strokeWidth2 - paddingX,
|
||||
top - scaleOffsetY - strokeWidth2 - paddingY);
|
||||
left - scaleOffset - strokeWidth2 - padding,
|
||||
top - scaleOffset - strokeWidth2 - padding);
|
||||
|
||||
// top-right
|
||||
this._drawControl('tr', ctx, methodName,
|
||||
left + width - scaleOffsetX + strokeWidth2 + paddingX,
|
||||
top - scaleOffsetY - strokeWidth2 - paddingY);
|
||||
left + width - scaleOffset + strokeWidth2 + padding,
|
||||
top - scaleOffset - strokeWidth2 - padding);
|
||||
|
||||
// bottom-left
|
||||
this._drawControl('bl', ctx, methodName,
|
||||
left - scaleOffsetX - strokeWidth2 - paddingX,
|
||||
top + height + scaleOffsetSizeY + strokeWidth2 + paddingY);
|
||||
left - scaleOffset - strokeWidth2 - padding,
|
||||
top + height + scaleOffsetSize + strokeWidth2 + padding);
|
||||
|
||||
// bottom-right
|
||||
this._drawControl('br', ctx, methodName,
|
||||
left + width + scaleOffsetSizeX + strokeWidth2 + paddingX,
|
||||
top + height + scaleOffsetSizeY + strokeWidth2 + paddingY);
|
||||
left + width + scaleOffsetSize + strokeWidth2 + padding,
|
||||
top + height + scaleOffsetSize + strokeWidth2 + padding);
|
||||
|
||||
if (!this.get('lockUniScaling')) {
|
||||
|
||||
// middle-top
|
||||
this._drawControl('mt', ctx, methodName,
|
||||
left + width/2 - scaleOffsetX,
|
||||
top - scaleOffsetY - strokeWidth2 - paddingY);
|
||||
left + width/2 - scaleOffset,
|
||||
top - scaleOffset - strokeWidth2 - padding);
|
||||
|
||||
// middle-bottom
|
||||
this._drawControl('mb', ctx, methodName,
|
||||
left + width/2 - scaleOffsetX,
|
||||
top + height + scaleOffsetSizeY + strokeWidth2 + paddingY);
|
||||
left + width/2 - scaleOffset,
|
||||
top + height + scaleOffsetSize + strokeWidth2 + padding);
|
||||
|
||||
// middle-right
|
||||
this._drawControl('mr', ctx, methodName,
|
||||
left + width + scaleOffsetSizeX + strokeWidth2 + paddingX,
|
||||
top + height/2 - scaleOffsetY);
|
||||
left + width + scaleOffsetSize + strokeWidth2 + padding,
|
||||
top + height/2 - scaleOffset);
|
||||
|
||||
// middle-left
|
||||
this._drawControl('ml', ctx, methodName,
|
||||
left - scaleOffsetX - strokeWidth2 - paddingX,
|
||||
top + height/2 - scaleOffsetY);
|
||||
left - scaleOffset - strokeWidth2 - padding,
|
||||
top + height/2 - scaleOffset);
|
||||
}
|
||||
|
||||
// middle-top-rotate
|
||||
if (this.hasRotatingPoint) {
|
||||
this._drawControl('mtr', ctx, methodName,
|
||||
left + width/2 - scaleOffsetX,
|
||||
left + width/2 - scaleOffset,
|
||||
this.flipY
|
||||
? (top + height + (this.rotatingPointOffset / this.scaleY) - this.cornerSize/this.scaleX/2 + strokeWidth2 + paddingY)
|
||||
: (top - (this.rotatingPointOffset / this.scaleY) - this.cornerSize/this.scaleY/2 - strokeWidth2 - paddingY));
|
||||
? (top + height + this.rotatingPointOffset - this.cornerSize/2 + strokeWidth2 + padding)
|
||||
: (top - this.rotatingPointOffset - this.cornerSize/2 - strokeWidth2 - padding));
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
|
|
@ -403,12 +408,11 @@
|
|||
* @private
|
||||
*/
|
||||
_drawControl: function(control, ctx, methodName, left, top) {
|
||||
var sizeX = this.cornerSize / this.scaleX,
|
||||
sizeY = this.cornerSize / this.scaleY;
|
||||
var size = this.cornerSize;
|
||||
|
||||
if (this.isControlVisible(control)) {
|
||||
isVML || this.transparentCorners || ctx.clearRect(left, top, sizeX, sizeY);
|
||||
ctx[methodName](left, top, sizeX, sizeY);
|
||||
isVML || this.transparentCorners || ctx.clearRect(left, top, size, size);
|
||||
ctx[methodName](left, top, size, size);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@
|
|||
parsedAttributes.left = 0;
|
||||
}
|
||||
if (!('top' in parsedAttributes)) {
|
||||
parsedAttributes.top = 0
|
||||
parsedAttributes.top = 0;
|
||||
}
|
||||
if (!('transformMatrix' in parsedAttributes)) {
|
||||
parsedAttributes.left -= (options.width / 2);
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@
|
|||
}
|
||||
this._setOpacityIfSame();
|
||||
|
||||
this.setCoords(true);
|
||||
this.setCoords();
|
||||
this.saveCoords();
|
||||
},
|
||||
|
||||
|
|
@ -114,8 +114,10 @@
|
|||
*/
|
||||
addWithUpdate: function(object) {
|
||||
this._restoreObjectsState();
|
||||
this._objects.push(object);
|
||||
object.group = this;
|
||||
if (object) {
|
||||
this._objects.push(object);
|
||||
object.group = this;
|
||||
}
|
||||
// since _restoreObjectsState set objects inactive
|
||||
this.forEachObject(this._setObjectActive, this);
|
||||
this._calcBounds();
|
||||
|
|
@ -213,15 +215,12 @@
|
|||
/**
|
||||
* Renders instance on a given context
|
||||
* @param {CanvasRenderingContext2D} ctx context to render instance on
|
||||
* @param {Boolean} [noTransform] When true, context is not transformed
|
||||
*/
|
||||
render: function(ctx, noTransform) {
|
||||
render: function(ctx) {
|
||||
// do not render if object is not visible
|
||||
if (!this.visible) return;
|
||||
|
||||
ctx.save();
|
||||
this.transform(ctx);
|
||||
|
||||
this.clipTo && fabric.util.clipContext(this, ctx);
|
||||
|
||||
// the array is now sorted in order of highest first, so start from end
|
||||
|
|
@ -231,31 +230,34 @@
|
|||
|
||||
this.clipTo && ctx.restore();
|
||||
|
||||
if (!noTransform && this.active) {
|
||||
this.drawBorders(ctx);
|
||||
this.drawControls(ctx);
|
||||
}
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders controls and borders for the object
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
* @param {Boolean} [noTransform] When true, context is not transformed
|
||||
*/
|
||||
_renderControls: function(ctx, noTransform) {
|
||||
this.callSuper('_renderControls', ctx, noTransform);
|
||||
for (var i = 0, len = this._objects.length; i < len; i++) {
|
||||
this._objects[i]._renderControls(ctx);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_renderObject: function(object, ctx) {
|
||||
|
||||
var originalScaleFactor = object.borderScaleFactor,
|
||||
originalHasRotatingPoint = object.hasRotatingPoint,
|
||||
groupScaleFactor = Math.max(this.scaleX, this.scaleY);
|
||||
var originalHasRotatingPoint = object.hasRotatingPoint;
|
||||
|
||||
// do not render if object is not visible
|
||||
if (!object.visible) return;
|
||||
|
||||
object.borderScaleFactor = groupScaleFactor;
|
||||
object.hasRotatingPoint = false;
|
||||
|
||||
object.render(ctx);
|
||||
|
||||
object.borderScaleFactor = originalScaleFactor;
|
||||
object.hasRotatingPoint = originalHasRotatingPoint;
|
||||
},
|
||||
|
||||
|
|
@ -450,20 +452,17 @@
|
|||
* @private
|
||||
*/
|
||||
_getBounds: function(aX, aY, onlyWidthHeight) {
|
||||
var minX = min(aX),
|
||||
maxX = max(aX),
|
||||
minY = min(aY),
|
||||
maxY = max(aY),
|
||||
width = (maxX - minX) || 0,
|
||||
height = (maxY - minY) || 0,
|
||||
obj = {
|
||||
width: width,
|
||||
height: height
|
||||
var ivt = fabric.util.invertTransform(this.getViewportTransform()),
|
||||
minXY = fabric.util.transformPoint(new fabric.Point(min(aX), min(aY)), ivt),
|
||||
maxXY = fabric.util.transformPoint(new fabric.Point(max(aX), max(aY)), ivt),
|
||||
obj = {
|
||||
width: (maxXY.x - minXY.x) || 0,
|
||||
height: (maxXY.y - minXY.y) || 0
|
||||
};
|
||||
|
||||
if (!onlyWidthHeight) {
|
||||
obj.left = (minX + width / 2) || 0;
|
||||
obj.top = (minY + height / 2) || 0;
|
||||
obj.left = (minXY.x + maxXY.x) / 2 || 0;
|
||||
obj.top = (minXY.y + maxXY.y) / 2 || 0;
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -122,7 +122,6 @@
|
|||
if (!this.visible) return;
|
||||
|
||||
ctx.save();
|
||||
|
||||
var m = this.transformMatrix,
|
||||
isInPathGroup = this.group && this.group.type === 'path-group';
|
||||
|
||||
|
|
@ -140,7 +139,6 @@
|
|||
ctx.translate(this.width/2, this.height/2);
|
||||
}
|
||||
|
||||
ctx.save();
|
||||
this._setShadow(ctx);
|
||||
this.clipTo && fabric.util.clipContext(this, ctx);
|
||||
this._render(ctx);
|
||||
|
|
@ -150,12 +148,6 @@
|
|||
this._renderStroke(ctx);
|
||||
this.clipTo && ctx.restore();
|
||||
ctx.restore();
|
||||
|
||||
if (this.active && !noTransform) {
|
||||
this.drawBorders(ctx);
|
||||
this.drawControls(ctx);
|
||||
}
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -732,6 +732,9 @@
|
|||
* @param {Boolean} fromLeft When true, context is transformed to object's top/left corner. This is used when rendering text on Node
|
||||
*/
|
||||
transform: function(ctx, fromLeft) {
|
||||
if (this.group) {
|
||||
this.group.transform(ctx, fromLeft);
|
||||
}
|
||||
ctx.globalAlpha = this.opacity;
|
||||
|
||||
var center = fromLeft ? this._getLeftTopCoords() : this.getCenterPoint();
|
||||
|
|
@ -920,6 +923,18 @@
|
|||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves viewportTransform from Object's canvas if possible
|
||||
* @method getViewportTransform
|
||||
* @memberOf fabric.Object.prototype
|
||||
* @return {Boolean} flipY value // TODO
|
||||
*/
|
||||
getViewportTransform: function() {
|
||||
if (this.canvas && this.canvas.viewportTransform)
|
||||
return this.canvas.viewportTransform;
|
||||
return [1, 0, 0, 1, 0, 0];
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders an object on a specified context
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
|
|
@ -949,19 +964,14 @@
|
|||
this._render(ctx, noTransform);
|
||||
this.clipTo && ctx.restore();
|
||||
this._removeShadow(ctx);
|
||||
|
||||
this._restoreFillRule(ctx);
|
||||
|
||||
if (this.active && !noTransform) {
|
||||
this.drawBorders(ctx);
|
||||
this.drawControls(ctx);
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
_transform: function(ctx, noTransform) {
|
||||
var m = this.transformMatrix;
|
||||
|
||||
if (m && !this.group) {
|
||||
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
}
|
||||
|
|
@ -990,6 +1000,35 @@
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders controls and borders for the object
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
* @param {Boolean} [noTransform] When true, context is not transformed
|
||||
*/
|
||||
_renderControls: function(ctx, noTransform) {
|
||||
var v = this.getViewportTransform();
|
||||
|
||||
ctx.save();
|
||||
if (this.active && !noTransform) {
|
||||
var center;
|
||||
if (this.group) {
|
||||
center = fabric.util.transformPoint(this.group.getCenterPoint(), v);
|
||||
ctx.translate(center.x, center.y);
|
||||
ctx.rotate(degreesToRadians(this.group.angle));
|
||||
}
|
||||
center = fabric.util.transformPoint(this.getCenterPoint(), v, null != this.group);
|
||||
if (this.group) {
|
||||
center.x *= this.group.scaleX;
|
||||
center.y *= this.group.scaleY;
|
||||
}
|
||||
ctx.translate(center.x, center.y);
|
||||
ctx.rotate(degreesToRadians(this.angle));
|
||||
this.drawBorders(ctx);
|
||||
this.drawControls(ctx);
|
||||
}
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
|
|
|
|||
|
|
@ -453,6 +453,7 @@
|
|||
|
||||
ctx.save();
|
||||
var m = this.transformMatrix;
|
||||
|
||||
if (m) {
|
||||
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
}
|
||||
|
|
@ -470,11 +471,6 @@
|
|||
this._renderStroke(ctx);
|
||||
this.clipTo && ctx.restore();
|
||||
this._removeShadow(ctx);
|
||||
|
||||
if (!noTransform && this.active) {
|
||||
this.drawBorders(ctx);
|
||||
this.drawControls(ctx);
|
||||
}
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -77,10 +77,10 @@
|
|||
ctx.save();
|
||||
|
||||
var m = this.transformMatrix;
|
||||
|
||||
if (m) {
|
||||
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
}
|
||||
|
||||
this.transform(ctx);
|
||||
|
||||
this._setShadow(ctx);
|
||||
|
|
@ -90,11 +90,6 @@
|
|||
}
|
||||
this.clipTo && ctx.restore();
|
||||
this._removeShadow(ctx);
|
||||
|
||||
if (this.active) {
|
||||
this.drawBorders(ctx);
|
||||
this.drawControls(ctx);
|
||||
}
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -319,7 +319,6 @@
|
|||
this.setOptions(options);
|
||||
this.__skipDimension = false;
|
||||
this._initDimensions();
|
||||
this.setCoords();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -760,9 +759,8 @@
|
|||
/**
|
||||
* Renders text instance on a specified context
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
* @param {Boolean} [noTransform] When true, context is not transformed
|
||||
*/
|
||||
render: function(ctx, noTransform) {
|
||||
render: function(ctx) {
|
||||
// do not render if object is not visible
|
||||
if (!this.visible) return;
|
||||
|
||||
|
|
@ -772,10 +770,6 @@
|
|||
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
}
|
||||
this._render(ctx);
|
||||
if (!noTransform && this.active) {
|
||||
this.drawBorders(ctx);
|
||||
this.drawControls(ctx);
|
||||
}
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -133,6 +133,13 @@
|
|||
*/
|
||||
imageSmoothingEnabled: true,
|
||||
|
||||
/**
|
||||
* The transformation (in the format of Canvas transform) which focuses the viewport
|
||||
* @type Array
|
||||
* @default
|
||||
*/
|
||||
viewportTransform: [1, 0, 0, 1, 0, 0],
|
||||
|
||||
/**
|
||||
* Callback; invoked right before object is about to be scaled/rotated
|
||||
* @param {fabric.Object} target Object that's about to be scaled/rotated
|
||||
|
|
@ -526,6 +533,92 @@
|
|||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns canvas zoom level
|
||||
* @return {Number}
|
||||
*/
|
||||
getZoom: function () {
|
||||
return Math.sqrt(this.viewportTransform[0] * this.viewportTransform[3]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets viewport transform of this canvas instance
|
||||
* @param {Array} vpt the transform in the form of context.transform
|
||||
* @return {fabric.Canvas} instance
|
||||
* @chainable true
|
||||
*/
|
||||
setViewportTransform: function (vpt) {
|
||||
this.viewportTransform = vpt;
|
||||
this.renderAll();
|
||||
for (var i = 0, len = this._objects.length; i < len; i++) {
|
||||
this._objects[i].setCoords();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets zoom level of this canvas instance, zoom centered around point
|
||||
* @param {fabric.Point} point to zoom with respect to
|
||||
* @param {Number} value to set zoom to, less than 1 zooms out
|
||||
* @return {fabric.Canvas} instance
|
||||
* @chainable true
|
||||
*/
|
||||
zoomToPoint: function (point, value) {
|
||||
// TODO: just change the scale, preserve other transformations
|
||||
var before = point;
|
||||
point = fabric.util.transformPoint(point, fabric.util.invertTransform(this.viewportTransform));
|
||||
this.viewportTransform[0] = value;
|
||||
this.viewportTransform[3] = value;
|
||||
var after = fabric.util.transformPoint(point, this.viewportTransform);
|
||||
this.viewportTransform[4] += before.x - after.x;
|
||||
this.viewportTransform[5] += before.y - after.y;
|
||||
this.renderAll();
|
||||
for (var i = 0, len = this._objects.length; i < len; i++) {
|
||||
this._objects[i].setCoords();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets zoom level of this canvas instance
|
||||
* @param {Number} value to set zoom to, less than 1 zooms out
|
||||
* @return {fabric.Canvas} instance
|
||||
* @chainable true
|
||||
*/
|
||||
setZoom: function (value) {
|
||||
this.zoomToPoint(new fabric.Point(0, 0), value);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Pan viewport so as to place point at top left corner of canvas
|
||||
* @param {fabric.Point} point to move to
|
||||
* @return {fabric.Canvas} instance
|
||||
* @chainable true
|
||||
*/
|
||||
absolutePan: function (point) {
|
||||
this.viewportTransform[4] = -point.x;
|
||||
this.viewportTransform[5] = -point.y;
|
||||
this.renderAll();
|
||||
for (var i = 0, len = this._objects.length; i < len; i++) {
|
||||
this._objects[i].setCoords();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Pans viewpoint relatively
|
||||
* @param {fabric.Point} point (position vector) to move by
|
||||
* @return {fabric.Canvas} instance
|
||||
* @chainable true
|
||||
*/
|
||||
relativePan: function (point) {
|
||||
return this.absolutePan(new fabric.Point(
|
||||
-point.x - this.viewportTransform[4],
|
||||
-point.y - this.viewportTransform[5]
|
||||
));
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns <canvas> element corresponding to this instance
|
||||
* @return {HTMLCanvasElement}
|
||||
|
|
@ -558,17 +651,13 @@
|
|||
*/
|
||||
_draw: function (ctx, object) {
|
||||
if (!object) return;
|
||||
|
||||
if (this.controlsAboveOverlay) {
|
||||
var hasBorders = object.hasBorders, hasControls = object.hasControls;
|
||||
object.hasBorders = object.hasControls = false;
|
||||
object.render(ctx);
|
||||
object.hasBorders = hasBorders;
|
||||
object.hasControls = hasControls;
|
||||
}
|
||||
else {
|
||||
object.render(ctx);
|
||||
}
|
||||
|
||||
ctx.save();
|
||||
var v = this.viewportTransform;
|
||||
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
object.render(ctx);
|
||||
ctx.restore();
|
||||
if (!this.controlsAboveOverlay) object._renderControls(ctx);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -577,8 +666,8 @@
|
|||
*/
|
||||
_onObjectAdded: function(obj) {
|
||||
this.stateful && obj.setupState();
|
||||
obj.setCoords();
|
||||
obj.canvas = this;
|
||||
obj.setCoords();
|
||||
this.fire('object:added', { target: obj });
|
||||
obj.fire('added');
|
||||
},
|
||||
|
|
@ -647,7 +736,6 @@
|
|||
* @chainable
|
||||
*/
|
||||
renderAll: function (allOnTop) {
|
||||
|
||||
var canvasToDrawOn = this[(allOnTop === true && this.interactive) ? 'contextTop' : 'contextContainer'],
|
||||
activeGroup = this.getActiveGroup();
|
||||
|
||||
|
|
@ -746,7 +834,7 @@
|
|||
this.height);
|
||||
}
|
||||
if (this.backgroundImage) {
|
||||
this.backgroundImage.render(ctx);
|
||||
this._draw(ctx, this.backgroundImage);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -767,7 +855,7 @@
|
|||
this.height);
|
||||
}
|
||||
if (this.overlayImage) {
|
||||
this.overlayImage.render(ctx);
|
||||
this._draw(ctx, this.overlayImage);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,45 @@
|
|||
return new fabric.Point(rx, ry).addEquals(origin);
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply transform t to point p
|
||||
* @static
|
||||
* @memberOf fabric.util
|
||||
* @param {fabric.Point} p The point to transform
|
||||
* @param {Array} t The transform
|
||||
* @param {Boolean} [ignoreOffset] Indicates that the offset should not be applied
|
||||
* @return {fabric.Point} The transformed point
|
||||
*/
|
||||
transformPoint: function(p, t, ignoreOffset) {
|
||||
if (ignoreOffset) {
|
||||
return new fabric.Point(
|
||||
t[0] * p.x + t[1] * p.y,
|
||||
t[2] * p.x + t[3] * p.y
|
||||
);
|
||||
}
|
||||
return new fabric.Point(
|
||||
t[0] * p.x + t[1] * p.y + t[4],
|
||||
t[2] * p.x + t[3] * p.y + t[5]
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Invert transformation t
|
||||
* @static
|
||||
* @memberOf fabric.util
|
||||
* @param {Array} t The transform
|
||||
* @return {Array} The inverted transform
|
||||
*/
|
||||
invertTransform: function(t) {
|
||||
var r = t.slice(),
|
||||
a = 1 / (t[0] * t[3] - t[1] * t[2]);
|
||||
r = [a * t[3], -a * t[1], -a * t[2], a * t[0], 0, 0];
|
||||
var o = fabric.util.transformPoint({x: t[4], y: t[5]}, r);
|
||||
r[4] = -o.x;
|
||||
r[5] = -o.y;
|
||||
return r;
|
||||
},
|
||||
|
||||
/**
|
||||
* A wrapper around Number#toFixed, which contrary to native method returns number, not string.
|
||||
* @static
|
||||
|
|
|
|||
Loading…
Reference in a new issue