Implement zoom for brushes, various zoom fixes, remove canvasBorder.

This commit is contained in:
Tom French 2013-11-08 15:54:55 +00:00
parent 328f14f388
commit 88b589b3d6
12 changed files with 5063 additions and 556 deletions

2540
dist/all.js vendored

File diff suppressed because it is too large Load diff

2842
dist/all.require.js vendored

File diff suppressed because it is too large Load diff

View file

@ -76,6 +76,7 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
circles.push(circle);
}
var group = new fabric.Group(circles);
group.canvas = this.canvas;
this.canvas.add(group);
this.canvas.fire('path:created', { path: group });

View file

@ -141,6 +141,10 @@
* @private
*/
_getSVGPathData: function() {
var ivt = fabric.util.invertTransform(this.canvas.viewportTransform);
for (var i = 0, len = this._points.length; i < len; i++) {
this._points[i] = fabric.util.transformPoint(this._points[i], ivt);
}
this.box = this.getPathBoundingBox(this._points);
return this.convertPointsToSVGPath(
this._points, this.box.minx, this.box.maxx, this.box.miny, this.box.maxy);

View file

@ -110,6 +110,8 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
}
var group = new fabric.Group(rects);
group.canvas = this.canvas;
this.canvas.add(group);
this.canvas.fire('path:created', { path: group });
@ -145,9 +147,13 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
var ctx = this.canvas.contextTop;
ctx.fillStyle = this.color;
ctx.save();
var ivt = fabric.util.invertTransform(this.canvas.viewportTransform);
for (var i = 0, len = this.sprayChunkPoints.length; i < len; i++) {
var point = this.sprayChunkPoints[i];
var tpoint = fabric.util.transformPoint({x: point.x, y: point.y}, ivt);
point.x = tpoint.x;
point.y = tpoint.y;
if (typeof point.opacity !== 'undefined') {
ctx.globalAlpha = point.opacity;
}
@ -163,6 +169,7 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
this.sprayChunkPoints = [ ];
var x, y, width, radius = this.width / 2;
var vpt = this.canvas.viewportTransform;
for (var i = 0; i < this.density; i++) {
@ -178,8 +185,10 @@ 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 = fabric.util.transformPoint(point, vpt);
point.width = width
if (this.randomOpacity) {
point.opacity = fabric.util.getRandomInt(0, 100) / 100;

View file

@ -249,7 +249,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
@ -504,6 +504,7 @@
var isActiveLower = objects.indexOf(this._activeObject) < objects.indexOf(target);
var group = new fabric.Group(
isActiveLower ? [ target, this._activeObject ] : [ this._activeObject, target ]);
group.canvas = this;
this.setActiveGroup(group);
this._activeObject = null;
@ -783,6 +784,7 @@
}
else if (group.length > 1) {
group = new fabric.Group(group.reverse());
group.canvas = this;
this.setActiveGroup(group);
group.saveCoords();
this.fire('selection:created', { target: group });
@ -799,7 +801,7 @@
if (this.skipTargetFind) return;
var target,
pointer = this.getPointer(e);
pointer = this.getPointer(e, true);
if (this.controlsAboveOverlay &&
this.lastRenderedObjectWithControlsAboveOverlay &&
@ -839,7 +841,7 @@
}
}
for (var j = 0, len = possibleTargets.length; j < len; j++) {
pointer = this.getPointer(e);
pointer = this.getPointer(e, true);
var isTransparent = this.isTargetTransparent(possibleTargets[j], pointer.x, pointer.y);
if (!isTransparent) {
target = possibleTargets[j];
@ -856,8 +858,17 @@
* @param {Event} e
* @return {Object} object with "x" and "y" number values
*/
getPointer: function (e) {
var pointer = getPointer(e, this.upperCanvasEl);
getPointer: function (e, ignoreZoom, upperCanvasEl) {
if (!upperCanvasEl) {
upperCanvasEl = this.upperCanvasEl;
}
var pointer = getPointer(e, upperCanvasEl);
if (!ignoreZoom) {
pointer = fabric.util.transformPoint(
pointer,
fabric.util.invertTransform(this.viewportTransform)
);
}
return {
x: pointer.x - this._offset.left,

View file

@ -187,7 +187,7 @@
}
}
else {
pointer = this.getPointer(e);
pointer = this.getPointer(e, true);
}
render = this._shouldRender(target, pointer);
@ -235,7 +235,7 @@
if (this.clipTo) {
fabric.util.clipContext(this, this.contextTop);
}
this.freeDrawingBrush.onMouseDown(this.getPointer(e));
this.freeDrawingBrush.onMouseDown(this.getPointer(e, true));
this.fire('mouse:down', { e: e });
},
@ -261,7 +261,7 @@
if (this._currentTransform) return;
var target = this.findTarget(e),
pointer = this.getPointer(e),
pointer = this.getPointer(e, true),
corner,
render;
@ -362,8 +362,7 @@
if (this.isDrawingMode) {
if (this._isCurrentlyDrawing) {
pointer = this.getPointer(e);
this.freeDrawingBrush.onMouseMove(pointer);
this.freeDrawingBrush.onMouseMove(this.getPointer(e, true));
}
this.upperCanvasEl.style.cursor = this.freeDrawingCursor;
this.fire('mouse:move', { e: e });
@ -374,10 +373,10 @@
// We initially clicked in an empty area, so we draw a box for multiple selection.
if (groupSelector) {
pointer = getPointer(e, this.upperCanvasEl);
pointer = this.getPointer(e, true);
groupSelector.left = pointer.x - this._offset.left - groupSelector.ex;
groupSelector.top = pointer.y - this._offset.top - groupSelector.ey;
groupSelector.left = pointer.x - groupSelector.ex;
groupSelector.top = pointer.y - groupSelector.ey;
this.renderTop();
}
else if (!this._currentTransform) {

View file

@ -306,10 +306,21 @@
var strokeWidth = this.strokeWidth > 1 ? this.strokeWidth : 0,
padding = this.padding,
theta = degreesToRadians(this.angle);
theta = degreesToRadians(this.angle),
vpt;
if (this.canvas) {
vpt = this.canvas.viewportTransform;
}
if (!vpt) { // TODO
vpt = [1, 0, 0, 1, 0, 0];
};
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) {
@ -329,42 +340,32 @@
cosTh = Math.cos(theta),
coords = this.getCenterPoint(),
wh = new fabric.Point(this.currentWidth, this.currentHeight);
var tl = {
x: coords.x - offsetX,
y: coords.y - offsetY
};
var tr = {
x: tl.x + (wh.x * cosTh),
y: tl.y + (wh.x * sinTh)
};
var br = {
x: tr.x - (wh.y * sinTh),
y: tr.y + (wh.y * cosTh)
};
var bl = {
x: tl.x - (wh.y * sinTh),
y: tl.y + (wh.y * cosTh)
};
var ml = {
x: tl.x - (wh.y/2 * sinTh),
y: tl.y + (wh.y/2 * cosTh)
};
var mt = {
x: tl.x + (wh.x/2 * cosTh),
y: tl.y + (wh.x/2 * sinTh)
};
var mr = {
x: tr.x - (wh.y/2 * sinTh),
y: tr.y + (wh.y/2 * cosTh)
};
var mb = {
x: bl.x + (wh.x/2 * cosTh),
y: bl.y + (wh.x/2 * sinTh)
};
var mtr = {
x: mt.x,
y: mt.y
};
var _tl = new fabric.Point(coords.x - offsetX, coords.y - offsetY);
var _tr = new fabric.Point(_tl.x + (wh.x * cosTh), _tl.y + (wh.x * sinTh));
var _bl = new fabric.Point(_tl.x - (wh.y * sinTh), _tl.y + (wh.y * cosTh));
var _mt = new fabric.Point(_tl.x + (wh.x/2 * cosTh), _tl.y + (wh.x/2 * sinTh));
var tl = f(_tl);
var tr = f(_tr);
var br = f(new fabric.Point(_tr.x - (wh.y * sinTh), _tr.y + (wh.y * cosTh)));
var bl = f(_bl);
var ml = f(new fabric.Point(_tl.x - (wh.y/2 * sinTh), _tl.y + (wh.y/2 * cosTh)));
var mt = f(_mt);
var mr = f(new fabric.Point(_tr.x - (wh.y/2 * sinTh), _tr.y + (wh.y/2 * cosTh)));
var mb = f(new fabric.Point(_bl.x + (wh.x/2 * cosTh), _bl.y + (wh.x/2 * sinTh)));
var mtr = f(new fabric.Point(_mt.x, _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
@ -389,22 +390,6 @@
mtr: mtr
};
var tform;
if (typeof this.canvas == 'undefined') {
if (this.type == 'group') {
tform = this._objects[0].canvas.viewportTransform;
}
else {
tform = [1, 0, 0, 1, 0, 0];
}
}
else {
tform = this.canvas.viewportTransform;
}
for (c in this.oCoords) {
this.oCoords[c] = fabric.util.transformPoint(this.oCoords[c], tform);
}
// set coordinates of the draggable boxes in the corners used to scale/rotate the image
this._setCornerCoords && this._setCornerCoords();

View file

@ -15,9 +15,9 @@
_findTargetCorner: function(e, offset) {
if (!this.hasControls || !this.active) return false;
var pointer = getPointer(e, this.canvas.upperCanvasEl),
ex = pointer.x - offset.left,
ey = pointer.y - offset.top,
var pointer = this.canvas.getPointer(e, true),
ex = pointer.x,
ey = pointer.y,
xPoints,
lines;
@ -267,15 +267,16 @@
ctx.lineWidth = 1 / this.borderScaleFactor;
var wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), this.canvas.viewportTransform, true),
sxy = fabric.util.transformPoint(new fabric.Point(scaleX, scaleY), this.canvas.viewportTransform, true),
var vpt = this.canvas.viewportTransform,
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.get('group')) {
w = w * this.get('group').scaleX;
h = h * this.get('group').scaleY;
if (this.group) {
w = w * this.group.scaleX;
h = h * this.group.scaleY;
}
ctx.strokeRect(

View file

@ -53,6 +53,7 @@
this._objects = objects || [];
for (var i = this._objects.length; i--; ) {
this._objects[i].group = this;
this._objects[i].setCoords();
}
this.originalState = { };
@ -216,13 +217,7 @@
if (!this.visible) return;
ctx.save();
var v;
if (this.canvas) {
v = this.canvas.viewportTransform;
}
else {
v = [1, 0, 0, 1, 0, 0]; // TODO: this isn't a solution
}
var v = this.canvas.viewportTransform;
var sxy = fabric.util.transformPoint(
new fabric.Point(this.scaleX, this.scaleY),
@ -403,10 +398,9 @@
_calcBounds: function() {
var aX = [],
aY = [],
minX, minY, maxX, maxY, o, width, height, minXY, maxXY, ivt, // TODO: cleanup
minX, minY, maxX, maxY, o, width, height, minXY, maxXY,
i = 0,
len = this._objects.length,
canvas = this._objects[0].canvas;
len = this._objects.length;
for (; i < len; ++i) {
o = this._objects[i];
@ -416,20 +410,18 @@
aY.push(o.oCoords[prop].y);
}
}
var ivt = fabric.util.invertTransform(canvas.viewportTransform);
minXY = new fabric.Point(min(aX), min(aY));
maxXY = new fabric.Point(max(aX), max(aY));
// TODO: cleanup
ivt = fabric.util.invertTransform(canvas.viewportTransform);
this.width = (maxXY.x - minXY.x) || 0;
this.height = (maxXY.y - minXY.y) || 0;
// TODO: cleanup
minXY = fabric.util.transformPoint(minXY, ivt);
maxXY = fabric.util.transformPoint(maxXY, ivt);
this.width = (maxXY.x - minXY.x) || 0;
this.height = (maxXY.y - minXY.y) || 0;
this.left = (minXY.x + maxXY.x) / 2 || 0;
this.top = (minXY.y + maxXY.y) / 2 || 0;
},

View file

@ -730,6 +730,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();
@ -1027,9 +1030,6 @@
}
if (!noTransform) {
if (this.group) {
this.group.transform(ctx);
}
this.transform(ctx);
}

View file

@ -146,13 +146,6 @@
*/
viewportTransform: [1, 0, 0, 1, 0, 0],
/**
* Color of canvas border
* @type String
* @default
*/
canvasBorderColor: '',
/**
* Callback; invoked right before object is about to be scaled/rotated
* @param {fabric.Object} target Object that's about to be scaled/rotated
@ -474,6 +467,10 @@
// TODO: just change the scale, preserve other transformations
this.viewportTransform[0] = value;
this.viewportTransform[3] = value;
this.renderAll();
for (var i = 0, len = this._objects.length; i < len; i++) {
this._objects[i].setCoords();
}
return this;
},
@ -491,6 +488,7 @@
);
this.viewportTransform[4] = x - wh.x/2;
this.viewportTransform[5] = y - wh.y/2;
this.renderAll();
return this;
},
@ -554,8 +552,14 @@
*/
_onObjectAdded: function(obj) {
this.stateful && obj.setupState();
obj.setCoords();
obj.canvas = this;
obj.setCoords();
if (obj._objects) {
for (var i = 0, len = obj._objects.length; i < len; i++) {
obj._objects[i].canvas = this;
obj._objects[i].setCoords();
}
}
this.fire('object:added', { target: obj });
obj.fire('added');
},
@ -657,10 +661,6 @@
if (typeof this.backgroundImage === 'object') {
this._drawBackroundImage(canvasToDrawOn);
}
if (this.canvasBorderColor) {
this._drawCanvasBorder(canvasToDrawOn);
}
var activeGroup = this.getActiveGroup();
for (var i = 0, length = this._objects.length; i < length; ++i) {
@ -717,23 +717,6 @@
canvasToDrawOn.restore();
},
/**
* @private
* @param {CanvasRenderingContext2D} canvasToDrawOn Context to render on
*/
_drawCanvasBorder: function(canvasToDrawOn) {
var xy = fabric.util.transformPoint(new fabric.Point(0, 0), this.viewportTransform),
wh = fabric.util.transformPoint(
new fabric.Point(this.getWidth(), this.getHeight()),
this.viewportTransform, true
);
canvasToDrawOn.save();
canvasToDrawOn.lineWidth = 1;
canvasToDrawOn.strokeStyle = this.canvasBorderColor;
canvasToDrawOn.strokeRect(xy.x - 1.5, xy.y - 1.5, wh.x + 2, wh.y + 2);
canvasToDrawOn.restore();
},
/**
* Method to render only the top canvas.
* Also used to render the group selection box.