Move canvas grouping logic to separate mixin/file

This commit is contained in:
kangax 2013-11-11 16:07:51 +01:00
parent e1e996ce87
commit 014109ced3
8 changed files with 592 additions and 524 deletions

View file

@ -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',

369
dist/all.js vendored
View file

@ -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 */ {
/**

10
dist/all.min.js vendored

File diff suppressed because one or more lines are too long

BIN
dist/all.min.js.gz vendored

Binary file not shown.

369
dist/all.require.js vendored
View file

@ -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 */ {
/**

View file

@ -5,8 +5,6 @@
radiansToDegrees = fabric.util.radiansToDegrees,
atan2 = Math.atan2,
abs = Math.abs,
min = Math.min,
max = Math.max,
STROKE_OFFSET = 0.5;
@ -374,6 +372,9 @@
return centerTransform ? !e.altKey : e.altKey;
},
/**
* @private
*/
_getOriginFromCorner: function(target, corner) {
var origin = {
x: target.originX,
@ -397,6 +398,9 @@
return origin;
},
/**
* @private
*/
_getActionFromCorner: function(target, corner) {
var action = 'drag';
if (corner) {
@ -455,96 +459,6 @@
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
@ -810,65 +724,6 @@
}
},
/**
* @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

View file

@ -235,26 +235,6 @@
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
*/
@ -374,8 +354,8 @@
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) {

View file

@ -0,0 +1,186 @@
(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;
}
});
})();