From 08d575422c12b4da14ad5fc3f4ba45b440a49af6 Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 19 Nov 2013 16:21:29 +0000 Subject: [PATCH] Fixes to zoom for groups and brushes. --- dist/all.js | 173 ++++++++++---------- dist/all.require.js | 192 ++++++++++------------- src/brushes/circle_brush.class.js | 10 +- src/brushes/pencil_brush.class.js | 8 +- src/brushes/spray_brush.class.js | 9 +- src/canvas.class.js | 5 +- src/mixins/canvas_events.mixin.js | 8 +- src/mixins/object_interactivity.mixin.js | 4 +- src/shapes/group.class.js | 35 +++-- src/shapes/image.class.js | 20 +-- src/shapes/object.class.js | 16 ++ src/shapes/path.class.js | 11 +- src/shapes/path_group.class.js | 11 +- src/shapes/text.class.js | 12 +- src/static_canvas.class.js | 24 ++- 15 files changed, 255 insertions(+), 283 deletions(-) diff --git a/dist/all.js b/dist/all.js index 3179ebb1..397041a6 100644 --- a/dist/all.js +++ b/dist/all.js @@ -8976,6 +8976,21 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */ return new fabric.Point(this.getWidth()/2 + x, this.getHeight()/2 + y); }, + /** + * 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 * @param {Number} value to set zoom to, less than 1 zooms out @@ -9008,6 +9023,9 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */ this.viewportTransform[4] = x - wh.x/2; this.viewportTransform[5] = y - wh.y/2; this.renderAll(); + for (var i = 0, len = this._objects.length; i < len; i++) { + this._objects[i].setCoords(); + } return this; }, @@ -9072,13 +9090,15 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */ _onObjectAdded: function(obj) { this.stateful && obj.setupState(); obj.canvas = this; - obj.setCoords(); if (obj._objects) { + obj._calcBounds(); for (var i = 0, len = obj._objects.length; i < len; i++) { obj._objects[i].canvas = this; - obj._objects[i].setCoords(); + this._onObjectAdded(obj._objects[i]); } + obj._updateObjectsCoords() } + obj.setCoords(); this.fire('object:added', { target: obj }); obj.fire('added'); }, @@ -9949,6 +9969,9 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype */ _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]; @@ -9978,6 +10001,7 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype // the bezier control point ctx.lineTo(p1.x, p1.y); ctx.stroke(); + ctx.restore(); }, /** @@ -9986,10 +10010,6 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype * @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); @@ -10151,11 +10171,17 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric var point = this.addPoint(pointer); 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.fillStyle = point.fill; ctx.beginPath(); ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false); ctx.closePath(); ctx.fill(); + + ctx.restore(); }, /** @@ -10188,10 +10214,10 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric for (var i = 0, len = this.points.length; i < len; i++) { var point = this.points[i]; var circle = new fabric.Circle({ - radius: point.radius, + radius: this.points[i].radius, left: point.x, top: point.y, - fill: point.fill + fill: this.points[i].fill }); this.shadow && circle.setShadow(this.shadow); @@ -10382,14 +10408,13 @@ 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(); - var ivt = fabric.util.invertTransform(this.canvas.viewportTransform); + 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]; - 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; } @@ -10405,7 +10430,6 @@ 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++) { @@ -10423,7 +10447,6 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric } var point = new fabric.Point(x, y); - point = fabric.util.transformPoint(point, vpt); point.width = width if (this.randomOpacity) { @@ -11001,7 +11024,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab 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; @@ -11281,7 +11303,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab } 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 }); @@ -11516,6 +11537,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab this._activeGroup = group; if (group) { group.canvas = this; + group._calcBounds(); + group._updateObjectsCoords(); + group.setCoords(); group.set('active', true); } return this; @@ -11860,7 +11884,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab if (this.clipTo) { fabric.util.clipContext(this, this.contextTop); } - this.freeDrawingBrush.onMouseDown(this.getPointer(e, true)); + 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 }); }, @@ -11987,7 +12013,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab if (this.isDrawingMode) { if (this._isCurrentlyDrawing) { - this.freeDrawingBrush.onMouseMove(this.getPointer(e, true)); + 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; this.fire('mouse:move', { e: e }); @@ -13723,7 +13751,23 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati this.clipTo && ctx.restore(); this._removeShadow(ctx); ctx.restore(); + + this._renderControls(ctx, noTransform); + }, + /** + * 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; + if (this.canvas) { + v = this.canvas.viewportTransform; + } + else { + v = [1, 0, 0, 1, 0, 0]; // TODO: this isn't a solution + } ctx.save(); if (this.active && !noTransform) { var center; @@ -15141,8 +15185,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot sx= sxy.x, sy= sxy.y; if (this.group) { - w = w * this.group.scaleX; - h = h * this.group.scaleY; + w = w * this.group.scaleX; + h = h * this.group.scaleY; } ctx.strokeRect( @@ -17388,16 +17432,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot this._removeShadow(ctx); ctx.restore(); - ctx.save(); - if (!noTransform && this.active) { - var center; - center = fabric.util.transformPoint(this.getCenterPoint(), v); - ctx.translate(center.x, center.y); - ctx.rotate(fabric.util.degreesToRadians(this.angle)); - this.drawBorders(ctx); - this.drawControls(ctx); - } - ctx.restore(); + this.callSuper('_renderControls', ctx, noTransform); }, /** @@ -17752,16 +17787,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot this._removeShadow(ctx); ctx.restore(); - ctx.save(); - if (this.active) { - var center; - center = fabric.util.transformPoint(this.getCenterPoint(), v); - ctx.translate(center.x, center.y); - ctx.rotate(fabric.util.degreesToRadians(this.angle)); - this.drawBorders(ctx); - this.drawControls(ctx); - } - ctx.restore(); + this.callSuper('_renderControls', ctx); }, /** @@ -17964,25 +17990,26 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot initialize: function(objects, options) { options = options || { }; + // NOTE: all the coords calculations need to have a canvas before they make sense this._objects = objects || []; for (var i = this._objects.length; i--; ) { this._objects[i].group = this; - this._objects[i].setCoords(); + //this._objects[i].setCoords(); } this.originalState = { }; this.callSuper('initialize'); - this._calcBounds(); - this._updateObjectsCoords(); + //this._calcBounds(); + //this._updateObjectsCoords(); if (options) { extend(this, options); } this._setOpacityIfSame(); - this.setCoords(true); - this.saveCoords(); + //this.setCoords(true); + //this.saveCoords(); }, /** @@ -18130,12 +18157,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot // do not render if object is not visible if (!this.visible) return; - ctx.save(); var v = this.canvas.viewportTransform; - + ctx.save(); var sxy = fabric.util.transformPoint( new fabric.Point(this.scaleX, this.scaleY), - v, true), + v, + true + ), groupScaleFactor = Math.max(sxy.x, sxy.y); this.clipTo && fabric.util.clipContext(this, ctx); @@ -18157,13 +18185,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot } this.clipTo && ctx.restore(); - if (this.active && !noTransform) { - var center = fabric.util.transformPoint(this.getCenterPoint(), v); - ctx.translate(center.x, center.y); - ctx.rotate(degreesToRadians(this.angle)); - this.drawBorders(ctx); - this.drawControls(ctx); - } + this.callSuper('_renderControls', ctx, noTransform); ctx.restore(); }, @@ -18325,7 +18347,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot } } - var ivt = fabric.util.invertTransform(canvas.viewportTransform); + var ivt; + if (this.canvas) { + ivt = fabric.util.invertTransform(this.canvas.viewportTransform); + } + else { // BUG: this always happens when new groups are created + ivt = [1, 0, 0, 1, 0, 0]; + console.log('no canvas'); + } minXY = new fabric.Point(min(aX), min(aY)); maxXY = new fabric.Point(max(aX), max(aY)); @@ -18555,25 +18584,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot ctx.restore(); ctx.restore(); - 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(fabric.util.degreesToRadians(this.angle)); - this.drawBorders(ctx); - this.drawControls(ctx); - } - ctx.restore(); + this.callSuper('_renderControls', ctx, noTransform); }, /** @@ -20842,16 +20853,8 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]); this._render(ctx); ctx.restore(); - ctx.save(); - if (!noTransform && this.active) { - var center; - center = fabric.util.transformPoint(this.getCenterPoint(), v); - ctx.translate(center.x, center.y); - ctx.rotate(fabric.util.degreesToRadians(this.angle)); - this.drawBorders(ctx); - this.drawControls(ctx); - } - ctx.restore(); + + this.callSuper('_renderControls', ctx, noTransform); }, /** diff --git a/dist/all.require.js b/dist/all.require.js index d832fe79..deb5fad2 100644 --- a/dist/all.require.js +++ b/dist/all.require.js @@ -8665,13 +8665,6 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */ */ 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 @@ -8993,6 +8986,10 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */ // 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; }, @@ -9010,6 +9007,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */ ); this.viewportTransform[4] = x - wh.x/2; this.viewportTransform[5] = y - wh.y/2; + this.renderAll(); return this; }, @@ -9076,7 +9074,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */ obj.canvas = this; obj.setCoords(); if (obj._objects) { - for (var i = 0, len = obj._objects; i < len; i++) { + for (var i = 0, len = obj._objects.length; i < len; i++) { obj._objects[i].canvas = this; obj._objects[i].setCoords(); } @@ -9182,10 +9180,6 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */ 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) { @@ -9242,23 +9236,6 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */ 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. @@ -10369,6 +10346,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 }); @@ -10404,9 +10383,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; } @@ -10439,7 +10422,7 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric width = this.dotWidth; } - var point = fabric.point(x, y); + var point = new fabric.Point(x, y); point = fabric.util.transformPoint(point, vpt); point.width = width @@ -10899,7 +10882,10 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab var action = 'drag', corner, - pointer = getPointer(e, target.canvas.UpperCanvasEl); + pointer = fabric.util.transformPoint( + getPointer(e, this.upperCanvasEl), + fabric.util.invertTransform(this.viewportTransform) + ); corner = target._findTargetCorner(e, this._offset); if (corner) { @@ -12040,7 +12026,10 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab } else { // object is being transformed (scaled/rotated/moved/etc.) - pointer = this.getPointer(e); + pointer = fabric.util.transformPoint( + getPointer(e, this.upperCanvasEl), + fabric.util.invertTransform(this.viewportTransform) + ); var x = pointer.x, y = pointer.y, @@ -13404,6 +13393,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati * @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(); @@ -13701,9 +13693,6 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati } if (!noTransform) { - if (this.group) { - this.group.transform(ctx); - } this.transform(ctx); } @@ -14735,10 +14724,21 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati 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) { @@ -14758,42 +14758,32 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati 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 @@ -14818,17 +14808,6 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati mtr: mtr }; - var vpt; - if (this.canvas) { - vpt = this.canvas.viewportTransform; - } - if (!vpt) { // TODO - vpt = [1, 0, 0, 1, 0, 0]; - } - for (c in this.oCoords) { - this.oCoords[c] = fabric.util.transformPoint(this.oCoords[c], vpt); - } - // set coordinates of the draggable boxes in the corners used to scale/rotate the image this._setCornerCoords && this._setCornerCoords(); @@ -14902,9 +14881,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot _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; @@ -15154,22 +15133,16 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot ctx.lineWidth = 1 / this.borderScaleFactor; - var vpt = this.canvas.viewportTransform; - // debugging - if (!vpt) { - vpt = [1, 0, 0, 1, 0, 0] - console.log("No vpt! interactivity", this.canvas, this.get('canvas'), this); - } - - var wh = fabric.util.transformPoint(new fabric.Point(this.getWidth(), this.getHeight()), vpt, 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( @@ -17994,6 +17967,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot this._objects = objects || []; for (var i = this._objects.length; i--; ) { this._objects[i].group = this; + this._objects[i].setCoords(); } this.originalState = { }; @@ -18338,16 +18312,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot _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, - vpt; - if (this.canvas) { - vpt = this.canvas.viewportTransform; - } - if (!vpt) { // TODO: this always happens when new groups are created - vpt = [1, 0, 0, 1, 0, 0]; - } + len = this._objects.length; for (; i < len; ++i) { o = this._objects[i]; @@ -18357,19 +18324,18 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot aY.push(o.oCoords[prop].y); } } + + var ivt = fabric.util.invertTransform(canvas.viewportTransform) || [1, 0, 0, 1, 0, 0]; minXY = new fabric.Point(min(aX), min(aY)); maxXY = new fabric.Point(max(aX), max(aY)); - ivt = fabric.util.invertTransform(vpt); - this.width = (maxXY.x - minXY.x) || 0; - this.height = (maxXY.y - minXY.y) || 0; - 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; }, diff --git a/src/brushes/circle_brush.class.js b/src/brushes/circle_brush.class.js index f790b6d1..39794345 100644 --- a/src/brushes/circle_brush.class.js +++ b/src/brushes/circle_brush.class.js @@ -28,11 +28,17 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric var point = this.addPoint(pointer); 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.fillStyle = point.fill; ctx.beginPath(); ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false); ctx.closePath(); ctx.fill(); + + ctx.restore(); }, /** @@ -65,10 +71,10 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric for (var i = 0, len = this.points.length; i < len; i++) { var point = this.points[i]; var circle = new fabric.Circle({ - radius: point.radius, + radius: this.points[i].radius, left: point.x, top: point.y, - fill: point.fill + fill: this.points[i].fill }); this.shadow && circle.setShadow(this.shadow); diff --git a/src/brushes/pencil_brush.class.js b/src/brushes/pencil_brush.class.js index 464c5c4a..2cf8d156 100644 --- a/src/brushes/pencil_brush.class.js +++ b/src/brushes/pencil_brush.class.js @@ -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(); }, /** @@ -141,10 +145,6 @@ * @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); diff --git a/src/brushes/spray_brush.class.js b/src/brushes/spray_brush.class.js index 4ff8fd9a..80b0466e 100644 --- a/src/brushes/spray_brush.class.js +++ b/src/brushes/spray_brush.class.js @@ -146,14 +146,13 @@ 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(); - var ivt = fabric.util.invertTransform(this.canvas.viewportTransform); + 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]; - 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; } @@ -169,7 +168,6 @@ 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++) { @@ -187,7 +185,6 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric } var point = new fabric.Point(x, y); - point = fabric.util.transformPoint(point, vpt); point.width = width if (this.randomOpacity) { diff --git a/src/canvas.class.js b/src/canvas.class.js index 04990ac8..baa12014 100644 --- a/src/canvas.class.js +++ b/src/canvas.class.js @@ -504,7 +504,6 @@ 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; @@ -784,7 +783,6 @@ } 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 }); @@ -1019,6 +1017,9 @@ this._activeGroup = group; if (group) { group.canvas = this; + group._calcBounds(); + group._updateObjectsCoords(); + group.setCoords(); group.set('active', true); } return this; diff --git a/src/mixins/canvas_events.mixin.js b/src/mixins/canvas_events.mixin.js index 077fab1d..67d8babe 100644 --- a/src/mixins/canvas_events.mixin.js +++ b/src/mixins/canvas_events.mixin.js @@ -235,7 +235,9 @@ if (this.clipTo) { fabric.util.clipContext(this, this.contextTop); } - this.freeDrawingBrush.onMouseDown(this.getPointer(e, true)); + 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 }); }, @@ -362,7 +364,9 @@ if (this.isDrawingMode) { if (this._isCurrentlyDrawing) { - this.freeDrawingBrush.onMouseMove(this.getPointer(e, true)); + 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; this.fire('mouse:move', { e: e }); diff --git a/src/mixins/object_interactivity.mixin.js b/src/mixins/object_interactivity.mixin.js index ba5e7e5f..c5214f74 100644 --- a/src/mixins/object_interactivity.mixin.js +++ b/src/mixins/object_interactivity.mixin.js @@ -275,8 +275,8 @@ sx= sxy.x, sy= sxy.y; if (this.group) { - w = w * this.group.scaleX; - h = h * this.group.scaleY; + w = w * this.group.scaleX; + h = h * this.group.scaleY; } ctx.strokeRect( diff --git a/src/shapes/group.class.js b/src/shapes/group.class.js index 4de2cf60..c324a833 100644 --- a/src/shapes/group.class.js +++ b/src/shapes/group.class.js @@ -50,25 +50,26 @@ initialize: function(objects, options) { options = options || { }; + // NOTE: all the coords calculations need to have a canvas before they make sense this._objects = objects || []; for (var i = this._objects.length; i--; ) { this._objects[i].group = this; - this._objects[i].setCoords(); + //this._objects[i].setCoords(); } this.originalState = { }; this.callSuper('initialize'); - this._calcBounds(); - this._updateObjectsCoords(); + //this._calcBounds(); + //this._updateObjectsCoords(); if (options) { extend(this, options); } this._setOpacityIfSame(); - this.setCoords(true); - this.saveCoords(); + //this.setCoords(true); + //this.saveCoords(); }, /** @@ -216,12 +217,13 @@ // do not render if object is not visible if (!this.visible) return; - ctx.save(); var v = this.canvas.viewportTransform; - + ctx.save(); var sxy = fabric.util.transformPoint( new fabric.Point(this.scaleX, this.scaleY), - v, true), + v, + true + ), groupScaleFactor = Math.max(sxy.x, sxy.y); this.clipTo && fabric.util.clipContext(this, ctx); @@ -243,13 +245,7 @@ } this.clipTo && ctx.restore(); - if (this.active && !noTransform) { - var center = fabric.util.transformPoint(this.getCenterPoint(), v); - ctx.translate(center.x, center.y); - ctx.rotate(degreesToRadians(this.angle)); - this.drawBorders(ctx); - this.drawControls(ctx); - } + this.callSuper('_renderControls', ctx, noTransform); ctx.restore(); }, @@ -411,7 +407,14 @@ } } - var ivt = fabric.util.invertTransform(canvas.viewportTransform); + var ivt; + if (this.canvas) { + ivt = fabric.util.invertTransform(this.canvas.viewportTransform); + } + else { // BUG: this always happens when new groups are created + ivt = [1, 0, 0, 1, 0, 0]; + console.log('no canvas'); + } minXY = new fabric.Point(min(aX), min(aY)); maxXY = new fabric.Point(max(aX), max(aY)); diff --git a/src/shapes/image.class.js b/src/shapes/image.class.js index e16af047..8a25ccb5 100644 --- a/src/shapes/image.class.js +++ b/src/shapes/image.class.js @@ -135,25 +135,7 @@ ctx.restore(); ctx.restore(); - 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(fabric.util.degreesToRadians(this.angle)); - this.drawBorders(ctx); - this.drawControls(ctx); - } - ctx.restore(); + this.callSuper('_renderControls', ctx, noTransform); }, /** diff --git a/src/shapes/object.class.js b/src/shapes/object.class.js index dc6129f3..4275bf36 100644 --- a/src/shapes/object.class.js +++ b/src/shapes/object.class.js @@ -1060,7 +1060,23 @@ this.clipTo && ctx.restore(); this._removeShadow(ctx); ctx.restore(); + + this._renderControls(ctx, noTransform); + }, + /** + * 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; + if (this.canvas) { + v = this.canvas.viewportTransform; + } + else { + v = [1, 0, 0, 1, 0, 0]; // TODO: this isn't a solution + } ctx.save(); if (this.active && !noTransform) { var center; diff --git a/src/shapes/path.class.js b/src/shapes/path.class.js index 98be5a88..6fd33c85 100644 --- a/src/shapes/path.class.js +++ b/src/shapes/path.class.js @@ -476,16 +476,7 @@ this._removeShadow(ctx); ctx.restore(); - ctx.save(); - if (!noTransform && this.active) { - var center; - center = fabric.util.transformPoint(this.getCenterPoint(), v); - ctx.translate(center.x, center.y); - ctx.rotate(fabric.util.degreesToRadians(this.angle)); - this.drawBorders(ctx); - this.drawControls(ctx); - } - ctx.restore(); + this.callSuper('_renderControls', ctx, noTransform); }, /** diff --git a/src/shapes/path_group.class.js b/src/shapes/path_group.class.js index 18e528a3..54431d06 100644 --- a/src/shapes/path_group.class.js +++ b/src/shapes/path_group.class.js @@ -93,16 +93,7 @@ this._removeShadow(ctx); ctx.restore(); - ctx.save(); - if (this.active) { - var center; - center = fabric.util.transformPoint(this.getCenterPoint(), v); - ctx.translate(center.x, center.y); - ctx.rotate(fabric.util.degreesToRadians(this.angle)); - this.drawBorders(ctx); - this.drawControls(ctx); - } - ctx.restore(); + this.callSuper('_renderControls', ctx); }, /** diff --git a/src/shapes/text.class.js b/src/shapes/text.class.js index 3a03872d..ad737d7f 100644 --- a/src/shapes/text.class.js +++ b/src/shapes/text.class.js @@ -767,16 +767,8 @@ ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]); this._render(ctx); ctx.restore(); - ctx.save(); - if (!noTransform && this.active) { - var center; - center = fabric.util.transformPoint(this.getCenterPoint(), v); - ctx.translate(center.x, center.y); - ctx.rotate(fabric.util.degreesToRadians(this.angle)); - this.drawBorders(ctx); - this.drawControls(ctx); - } - ctx.restore(); + + this.callSuper('_renderControls', ctx, noTransform); }, /** diff --git a/src/static_canvas.class.js b/src/static_canvas.class.js index 9e22540b..a14794eb 100644 --- a/src/static_canvas.class.js +++ b/src/static_canvas.class.js @@ -457,6 +457,21 @@ return new fabric.Point(this.getWidth()/2 + x, this.getHeight()/2 + y); }, + /** + * 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 * @param {Number} value to set zoom to, less than 1 zooms out @@ -489,6 +504,9 @@ this.viewportTransform[4] = x - wh.x/2; this.viewportTransform[5] = y - wh.y/2; this.renderAll(); + for (var i = 0, len = this._objects.length; i < len; i++) { + this._objects[i].setCoords(); + } return this; }, @@ -553,13 +571,15 @@ _onObjectAdded: function(obj) { this.stateful && obj.setupState(); obj.canvas = this; - obj.setCoords(); if (obj._objects) { + obj._calcBounds(); for (var i = 0, len = obj._objects.length; i < len; i++) { obj._objects[i].canvas = this; - obj._objects[i].setCoords(); + this._onObjectAdded(obj._objects[i]); } + obj._updateObjectsCoords() } + obj.setCoords(); this.fire('object:added', { target: obj }); obj.fire('added'); },