diff --git a/build.js b/build.js index edd04b91..7dbb5f18 100644 --- a/build.js +++ b/build.js @@ -186,6 +186,7 @@ var filesToInclude = [ ifSpecifiedInclude('interaction', 'src/canvas.class.js'), ifSpecifiedInclude('interaction', 'src/mixins/canvas_events.mixin.js'), + ifSpecifiedInclude('interaction', 'src/mixins/canvas_grouping.mixin.js'), 'src/mixins/canvas_dataurl_exporter.mixin.js', diff --git a/dist/all.js b/dist/all.js index b589efd6..610713bc 100644 --- a/dist/all.js +++ b/dist/all.js @@ -8799,8 +8799,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab radiansToDegrees = fabric.util.radiansToDegrees, atan2 = Math.atan2, abs = Math.abs, - min = Math.min, - max = Math.max, STROKE_OFFSET = 0.5; @@ -9168,6 +9166,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab return centerTransform ? !e.altKey : e.altKey; }, + /** + * @private + */ _getOriginFromCorner: function(target, corner) { var origin = { x: target.originX, @@ -9191,6 +9192,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab return origin; }, + /** + * @private + */ _getActionFromCorner: function(target, corner) { var action = 'drag'; if (corner) { @@ -9249,96 +9253,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab this._resetCurrentTransform(e); }, - /** - * @private - * @param {Event} e Event object - * @param {fabric.Object} target - * @return {Boolean} - */ - _shouldHandleGroupLogic: function(e, target) { - var activeObject = this.getActiveObject(); - return e.shiftKey && - (this.getActiveGroup() || (activeObject && activeObject !== target)) - && this.selection; - }, - - /** - * @private - * @param {Event} e Event object - * @param {fabric.Object} target - */ - _handleGroupLogic: function (e, target) { - - if (target === this.getActiveGroup()) { - - // if it's a group, find target again, this time skipping group - target = this.findTarget(e, true); - - // if even object is not found, bail out - if (!target || target.isType('group')) { - return; - } - } - if (this.getActiveGroup()) { - this._updateActiveGroup(target, e); - } - else { - this._createActiveGroup(target, e); - } - - if (this._activeGroup) { - this._activeGroup.saveCoords(); - } - }, - - _updateActiveGroup: function(target, e) { - var activeGroup = this.getActiveGroup(); - - if (activeGroup.contains(target)) { - activeGroup.removeWithUpdate(target); - this._resetObjectTransform(activeGroup); - target.set('active', false); - - if (activeGroup.size() === 1) { - // remove group alltogether if after removal it only contains 1 object - this.discardActiveGroup(e); - // activate last remaining object - this.setActiveObject(activeGroup.item(0)); - return; - } - } - else { - activeGroup.addWithUpdate(target); - this._resetObjectTransform(activeGroup); - } - this.fire('selection:created', { target: activeGroup, e: e }); - activeGroup.set('active', true); - }, - - _createActiveGroup: function(target, e) { - // group does not exist - if (this._activeObject) { - // only if there's an active object - if (target !== this._activeObject) { - // and that object is not the actual target - var objects = this.getObjects(); - var isActiveLower = objects.indexOf(this._activeObject) < objects.indexOf(target); - var groupObjects = isActiveLower - ? [ target, this._activeObject ] - : [ this._activeObject, target ]; - - var group = new fabric.Group(groupObjects, { originX: 'center', originY: 'center' }); - - this.setActiveGroup(group); - this._activeObject = null; - var activeGroup = this.getActiveGroup(); - this.fire('selection:created', { target: activeGroup, e: e }); - } - } - // activate target object in any case - target.set('active', true); - }, - /** * Translates object by "setting" its left/top * @private @@ -9604,65 +9518,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab } }, - /** - * @private - * @param {Event} e mouse event - */ - _groupSelectedObjects: function (e) { - - var group = this._collectObjects(); - - // do not create group for 1 element only - if (group.length === 1) { - this.setActiveObject(group[0], e); - } - else if (group.length > 1) { - group = new fabric.Group(group.reverse(), { - originX: 'center', - originY: 'center' - }); - this.setActiveGroup(group, e); - group.saveCoords(); - this.fire('selection:created', { target: group }); - this.renderAll(); - } - }, - - /** - * @private - */ - _collectObjects: function() { - var group = [ ], - currentObject, - x1 = this._groupSelector.ex, - y1 = this._groupSelector.ey, - x2 = x1 + this._groupSelector.left, - y2 = y1 + this._groupSelector.top, - selectionX1Y1 = new fabric.Point(min(x1, x2), min(y1, y2)), - selectionX2Y2 = new fabric.Point(max(x1, x2), max(y1, y2)), - isClick = x1 === x2 && y1 === y2; - - for (var i = this._objects.length; i--; ) { - currentObject = this._objects[i]; - - if (!currentObject || !currentObject.selectable || !currentObject.visible) continue; - - if (currentObject.intersectsWithRect(selectionX1Y1, selectionX2Y2) || - currentObject.isContainedWithinRect(selectionX1Y1, selectionX2Y2) || - currentObject.containsPoint(selectionX1Y1) || - currentObject.containsPoint(selectionX2Y2) - ) { - currentObject.set('active', true); - group.push(currentObject); - - // only add one object if it's a click - if (isClick) break; - } - } - - return group; - }, - /** * Method that determines what object we are clicking on * @param {Event} e mouse event @@ -10260,26 +10115,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab target && target.fire('mouseup', { e: e }); }, - /** - * @private - */ - _maybeGroupObjects: function(e) { - if (this.selection && this._groupSelector) { - this._groupSelectedObjects(e); - } - - var activeGroup = this.getActiveGroup(); - if (activeGroup) { - activeGroup.setObjectsCoords(); - activeGroup.isMoving = false; - this._setCursor(this.defaultCursor); - } - - // clear selection and current transformation - this._groupSelector = null; - this._currentTransform = null; - }, - /** * @private */ @@ -10399,8 +10234,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab if (this._shouldClearSelection(e, target)) { this._clearSelection(e, target, pointer); } - else if (this._shouldHandleGroupLogic(e, target)) { - this._handleGroupLogic(e, target); + else if (this._shouldGroup(e, target)) { + this._handleGrouping(e, target); target = this.getActiveGroup(); } else if (target && target.selectable) { @@ -10683,6 +10518,194 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab })(); +(function(){ + + var min = Math.min, + max = Math.max; + + fabric.util.object.extend(fabric.Canvas.prototype, /** @lends fabric.Canvas.prototype */ { + + /** + * @private + * @param {Event} e Event object + * @param {fabric.Object} target + * @return {Boolean} + */ + _shouldGroup: function(e, target) { + var activeObject = this.getActiveObject(); + return e.shiftKey && + (this.getActiveGroup() || (activeObject && activeObject !== target)) + && this.selection; + }, + + /** + * @private + * @param {Event} e Event object + * @param {fabric.Object} target + */ + _handleGrouping: function (e, target) { + + if (target === this.getActiveGroup()) { + + // if it's a group, find target again, this time skipping group + target = this.findTarget(e, true); + + // if even object is not found, bail out + if (!target || target.isType('group')) { + return; + } + } + if (this.getActiveGroup()) { + this._updateActiveGroup(target, e); + } + else { + this._createActiveGroup(target, e); + } + + if (this._activeGroup) { + this._activeGroup.saveCoords(); + } + }, + + /** + * @private + */ + _updateActiveGroup: function(target, e) { + var activeGroup = this.getActiveGroup(); + + if (activeGroup.contains(target)) { + + activeGroup.removeWithUpdate(target); + this._resetObjectTransform(activeGroup); + target.set('active', false); + + if (activeGroup.size() === 1) { + // remove group alltogether if after removal it only contains 1 object + this.discardActiveGroup(e); + // activate last remaining object + this.setActiveObject(activeGroup.item(0)); + return; + } + } + else { + activeGroup.addWithUpdate(target); + this._resetObjectTransform(activeGroup); + } + this.fire('selection:created', { target: activeGroup, e: e }); + activeGroup.set('active', true); + }, + + /** + * @private + */ + _createActiveGroup: function(target, e) { + if (this._activeObject) { + // only if there's an active object + if (target !== this._activeObject) { + // and that object is not the actual target + var objects = this.getObjects(); + var isActiveLower = objects.indexOf(this._activeObject) < objects.indexOf(target); + var groupObjects = isActiveLower + ? [ target, this._activeObject ] + : [ this._activeObject, target ]; + + var group = new fabric.Group(groupObjects, { originX: 'center', originY: 'center' }); + + this.setActiveGroup(group); + this._activeObject = null; + + var activeGroup = this.getActiveGroup(); + + this.fire('selection:created', { target: activeGroup, e: e }); + } + } + // activate target object in any case + target.set('active', true); + }, + + /** + * @private + * @param {Event} e mouse event + */ + _groupSelectedObjects: function (e) { + + var group = this._collectObjects(); + + // do not create group for 1 element only + if (group.length === 1) { + this.setActiveObject(group[0], e); + } + else if (group.length > 1) { + group = new fabric.Group(group.reverse(), { + originX: 'center', + originY: 'center' + }); + this.setActiveGroup(group, e); + group.saveCoords(); + this.fire('selection:created', { target: group }); + this.renderAll(); + } + }, + + /** + * @private + */ + _collectObjects: function() { + var group = [ ], + currentObject, + x1 = this._groupSelector.ex, + y1 = this._groupSelector.ey, + x2 = x1 + this._groupSelector.left, + y2 = y1 + this._groupSelector.top, + selectionX1Y1 = new fabric.Point(min(x1, x2), min(y1, y2)), + selectionX2Y2 = new fabric.Point(max(x1, x2), max(y1, y2)), + isClick = x1 === x2 && y1 === y2; + + for (var i = this._objects.length; i--; ) { + currentObject = this._objects[i]; + + if (!currentObject || !currentObject.selectable || !currentObject.visible) continue; + + if (currentObject.intersectsWithRect(selectionX1Y1, selectionX2Y2) || + currentObject.isContainedWithinRect(selectionX1Y1, selectionX2Y2) || + currentObject.containsPoint(selectionX1Y1) || + currentObject.containsPoint(selectionX2Y2) + ) { + currentObject.set('active', true); + group.push(currentObject); + + // only add one object if it's a click + if (isClick) break; + } + } + + return group; + }, + + /** + * @private + */ + _maybeGroupObjects: function(e) { + if (this.selection && this._groupSelector) { + this._groupSelectedObjects(e); + } + + var activeGroup = this.getActiveGroup(); + if (activeGroup) { + activeGroup.setObjectsCoords(); + activeGroup.isMoving = false; + this._setCursor(this.defaultCursor); + } + + // clear selection and current transformation + this._groupSelector = null; + this._currentTransform = null; + } + }); + +})(); + + fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ { /** diff --git a/dist/all.min.js b/dist/all.min.js index a93f7211..04b6aee9 100644 --- a/dist/all.min.js +++ b/dist/all.min.js @@ -1,7 +1,7 @@ /* build: `node build.js modules=ALL exclude=gestures minifier=uglifyjs` *//*! Fabric.js Copyright 2008-2013, Printio (Juriy Zaytsev, Maxim Chernyak) */var fabric=fabric||{version:"1.3.10"};typeof exports!="undefined"&&(exports.fabric=fabric),typeof document!="undefined"&&typeof window!="undefined"?(fabric.document=document,fabric.window=window):(fabric.document=require("jsdom").jsdom("
"),fabric.window=fabric.document.createWindow()),fabric.isTouchSupported="ontouchstart"in fabric.document.documentElement,fabric.isLikelyNode=typeof Buffer!="undefined"&&typeof window=="undefined",fabric.SHARED_ATTRIBUTES=["transform","fill","fill-opacity","fill-rule","opacity","stroke","stroke-dasharray","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width"];var Cufon=function(){function r(e){var t=this.face=e.face;this.glyphs=e.glyphs,this.w=e.w,this.baseSize=parseInt(t["units-per-em"],10),this.family=t["font-family"].toLowerCase(),this.weight=t["font-weight"],this.style=t["font-style"]||"normal",this.viewBox=function(){var e=t.bbox.split(/\s+/),n={minX:parseInt(e[0],10),minY:parseInt(e[1],10),maxX:parseInt(e[2],10),maxY:parseInt(e[3],10)};return n.width=n.maxX-n.minX,n.height=n.maxY-n.minY,n.toString=function(){return[this.minX,this.minY,this.width,this.height].join(" ")},n}(),this.ascent=-parseInt(t.ascent,10),this.descent=-parseInt(t.descent,10),this.height=-this.ascent+this.descent}function i(){var e={},t={oblique:"italic",italic:"oblique"};this.add=function(t){(e[t.style]||(e[t.style]={}))[t.weight]=t},this.get=function(n,r){var i=e[n]||e[t[n]]||e.normal||e.italic||e.oblique;if(!i)return null;r={normal:400,bold:700}[r]||parseInt(r,10);if(i[r])return i[r];var s={1:1,99:0}[r%100],o=[],u,a;s===undefined&&(s=r>400),r==500&&(r=400);for(var f in i){f=parseInt(f,10);if(!u||fa)a=f;o.push(f)}return ra&&(r=a),o.sort(function(e,t){return(s?e>r&&t>r?e-1},complexity:function(){return this.getObjects().reduce(function(e,t){return e+=t.complexity?t.complexity():0,e},0)}},function(e){function r(e,t){var n=e.indexOf(t);return n!==-1&&e.splice(n,1),e}function i(e,t){return Math.floor(Math.random()*(t-e+1))+e}function o(e){return e*s}function u(e){return e/s}function a(e,t,n){var r=Math.sin(n),i=Math.cos(n);e.subtractEquals(t);var s=e.x*i-e.y*r,o=e.x*r+e.y*i;return(new fabric.Point(s,o)).addEquals(t)}function f(e,t){return parseFloat(Number(e).toFixed(t))}function l(){return!1}function c(e,t){return e=fabric.util.string.camelize(e.charAt(0).toUpperCase()+e.slice(1)),h(t)[e]}function h(t){if(!t)return fabric;var n=t.split("."),r=n.length,i=e||fabric.window;for(var s=0;s=t})}function r(e,t){return i(e,t,function(e,t){return e>>0,n=0,r;if(arguments.length>1)r=arguments[1];else do{if(n in this){r=this[n++];break}if(++n>=t)throw new TypeError}while(!0);for(;n0&&(i.status="Intersection"),i},t.Intersection.intersectPolygonPolygon=function(e,t){var r=new n,i=e.length;for(var s=0;s0&&(r.status="Intersection"),r},t.Intersection.intersectPolygonRectangle=function(e,r,i){var s=r.min(i),o=r.max(i),u=new t.Point(o.x,s.y),a=new t.Point(s.x,o.y),f=n.intersectLinePolygon(s,u,e),l=n.intersectLinePolygon(u,o,e),c=n.intersectLinePolygon(o,a,e),h=n.intersectLinePolygon(a,s,e),p=new n;return p.appendPoints(f.points),p.appendPoints(l.points),p.appendPoints(c.points),p.appendPoints(h.points),p.points.length>0&&(p.status="Intersection"),p}}(typeof exports!="undefined"?exports:this),function(e){"use strict";function n(e){e?this._tryParsingColor(e):this.setSource([0,0,0,1])}function r(e,t,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?e+(t-e)*6*n:n<.5?t:n<2/3?e+(t-e)*(2/3-n)*6:e}var t=e.fabric||(e.fabric={});if(t.Color){t.warn("fabric.Color is already defined.");return}t.Color=n,t.Color.prototype={_tryParsingColor:function(e){var t;e in n.colorNameMap&&(e=n.colorNameMap[e]),t=n.sourceFromHex(e),t||(t=n.sourceFromRgb(e)),t||(t=n.sourceFromHsl(e)),t&&this.setSource(t)},_rgbToHsl:function(e,n,r){e/=255,n/=255,r/=255;var i,s,o,u=t.util.array.max([e,n,r]),a=t.util.array.min([e,n,r]);o=(u+a)/2;if(u===a)i=s=0;else{var f=u-a;s=o>.5?f/(2-u-a):f/(u+a);switch(u){case e:i=(n-r)/f+(n