mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-03-16 22:10:32 +00:00
Move canvas grouping logic to separate mixin/file
This commit is contained in:
parent
e1e996ce87
commit
014109ced3
8 changed files with 592 additions and 524 deletions
1
build.js
1
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',
|
||||
|
||||
|
|
|
|||
369
dist/all.js
vendored
369
dist/all.js
vendored
|
|
@ -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
10
dist/all.min.js
vendored
File diff suppressed because one or more lines are too long
BIN
dist/all.min.js.gz
vendored
BIN
dist/all.min.js.gz
vendored
Binary file not shown.
369
dist/all.require.js
vendored
369
dist/all.require.js
vendored
|
|
@ -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 */ {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
186
src/mixins/canvas_grouping.mixin.js
Normal file
186
src/mixins/canvas_grouping.mixin.js
Normal 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;
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
||||
Loading…
Reference in a new issue