mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-05-19 02:51:07 +00:00
Remove activeGroup functionalities from Group, create ActiveSelection class (#4076)
This commit is contained in:
parent
91376732ac
commit
c9b562de9e
14 changed files with 693 additions and 438 deletions
1
build.js
1
build.js
|
|
@ -198,6 +198,7 @@ var filesToInclude = [
|
|||
'src/shapes/polygon.class.js',
|
||||
'src/shapes/path.class.js',
|
||||
'src/shapes/group.class.js',
|
||||
'src/shapes/active_selection.class.js',
|
||||
'src/shapes/image.class.js',
|
||||
|
||||
ifSpecifiedInclude('object_straightening', 'src/mixins/object_straightening.mixin.js'),
|
||||
|
|
|
|||
|
|
@ -328,25 +328,25 @@
|
|||
* @return {Array} objects to render immediately and pushes the other in the activeGroup.
|
||||
*/
|
||||
_chooseObjectsToRender: function() {
|
||||
var activeGroup = this.getActiveGroup(),
|
||||
activeObject = this.getActiveObject(),
|
||||
object, objsToRender = [], activeGroupObjects = [];
|
||||
var activeObjects = this.getActiveObjects(),
|
||||
object, objsToRender, activeGroupObjects;
|
||||
|
||||
if ((activeGroup || activeObject) && !this.preserveObjectStacking) {
|
||||
if (activeObjects.length > 0 && !this.preserveObjectStacking) {
|
||||
objsToRender = [];
|
||||
activeGroupObjects = [];
|
||||
for (var i = 0, length = this._objects.length; i < length; i++) {
|
||||
object = this._objects[i];
|
||||
if ((!activeGroup || !activeGroup.contains(object)) && object !== activeObject) {
|
||||
if (activeObjects.indexOf(object) === -1 ) {
|
||||
objsToRender.push(object);
|
||||
}
|
||||
else {
|
||||
activeGroupObjects.push(object);
|
||||
}
|
||||
}
|
||||
if (activeGroup) {
|
||||
activeGroup._set('_objects', activeGroupObjects);
|
||||
objsToRender.push(activeGroup);
|
||||
if (activeObjects.length > 1) {
|
||||
this._activeObject._objects = activeGroupObjects;
|
||||
}
|
||||
activeObject && objsToRender.push(activeObject);
|
||||
objsToRender.push.apply(objsToRender, activeGroupObjects);
|
||||
}
|
||||
else {
|
||||
objsToRender = this._objects;
|
||||
|
|
@ -449,7 +449,7 @@
|
|||
pointer = point || this.getPointer(e, ignoreZoom),
|
||||
xy;
|
||||
|
||||
if (target.group && target.group === this.getActiveGroup()) {
|
||||
if (target.group && target.group === this._activeObject && target.group.type === 'activeSelection') {
|
||||
xy = this._normalizePointer(target.group, pointer);
|
||||
}
|
||||
else {
|
||||
|
|
@ -511,16 +511,16 @@
|
|||
* @param {fabric.Object} target
|
||||
*/
|
||||
_shouldClearSelection: function (e, target) {
|
||||
var activeGroup = this.getActiveGroup(),
|
||||
activeObject = this.getActiveObject();
|
||||
|
||||
var activeObjects = this.getActiveObjects(),
|
||||
activeObject = this._activeObject;
|
||||
return (
|
||||
!target
|
||||
||
|
||||
(target &&
|
||||
activeGroup &&
|
||||
!activeGroup.contains(target) &&
|
||||
activeGroup !== target &&
|
||||
activeObject &&
|
||||
activeObjects.length > 1 &&
|
||||
activeObjects.indexOf(target) === -1 &&
|
||||
activeObject !== target &&
|
||||
!e[this.selectionKey])
|
||||
||
|
||||
(target && !target.evented)
|
||||
|
|
@ -1104,23 +1104,25 @@
|
|||
|
||||
var ignoreZoom = true,
|
||||
pointer = this.getPointer(e, ignoreZoom),
|
||||
activeGroup = this.getActiveGroup(),
|
||||
activeObject = this.getActiveObject(),
|
||||
activeObject = this._activeObject,
|
||||
aObjects = this.getActiveObjects(),
|
||||
activeTarget;
|
||||
// first check current group (if one exists)
|
||||
// active group does not check sub targets like normal groups.
|
||||
// if active group just exits.
|
||||
this.targets = [];
|
||||
if (activeGroup && !skipGroup && activeGroup === this._searchPossibleTargets([activeGroup], pointer)) {
|
||||
this._fireOverOutEvents(activeGroup, e);
|
||||
return activeGroup;
|
||||
}
|
||||
// if we hit the corner of an activeObject, let's return that.
|
||||
if (activeObject && activeObject._findTargetCorner(pointer)) {
|
||||
|
||||
if (aObjects.length > 1 && !skipGroup && activeObject === this._searchPossibleTargets([activeObject], pointer)) {
|
||||
this._fireOverOutEvents(activeObject, e);
|
||||
return activeObject;
|
||||
}
|
||||
if (activeObject && activeObject === this._searchPossibleTargets([activeObject], pointer)) {
|
||||
// if we hit the corner of an activeObject, let's return that.
|
||||
if (aObjects.length === 1 && activeObject._findTargetCorner(pointer)) {
|
||||
this._fireOverOutEvents(activeObject, e);
|
||||
return activeObject;
|
||||
}
|
||||
if (aObjects.length === 1 &&
|
||||
activeObject === this._searchPossibleTargets([activeObject], pointer)) {
|
||||
if (!this.preserveObjectStacking) {
|
||||
this._fireOverOutEvents(activeObject, e);
|
||||
return activeObject;
|
||||
|
|
@ -1129,7 +1131,6 @@
|
|||
activeTarget = activeObject;
|
||||
}
|
||||
}
|
||||
|
||||
var target = this._searchPossibleTargets(this._objects, pointer);
|
||||
if (e[this.altSelectionKey] && target && activeTarget && target !== activeTarget) {
|
||||
target = activeTarget;
|
||||
|
|
@ -1374,41 +1375,6 @@
|
|||
return this.upperCanvasEl;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} object
|
||||
*/
|
||||
_setActiveObject: function(object) {
|
||||
var obj = this._activeObject;
|
||||
if (obj) {
|
||||
obj.set('active', false);
|
||||
if (object !== obj && obj.onDeselect && typeof obj.onDeselect === 'function') {
|
||||
obj.onDeselect();
|
||||
}
|
||||
}
|
||||
this._activeObject = object;
|
||||
object.set('active', true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets given object as the only active object on canvas
|
||||
* @param {fabric.Object} object Object to set as an active one
|
||||
* @param {Event} [e] Event (passed along when firing "object:selected")
|
||||
* @return {fabric.Canvas} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
setActiveObject: function (object, e) {
|
||||
var currentActiveObject = this.getActiveObject();
|
||||
if (currentActiveObject && currentActiveObject !== object) {
|
||||
currentActiveObject.fire('deselected', { e: e });
|
||||
}
|
||||
this._setActiveObject(object);
|
||||
this.fire('object:selected', { target: object, e: e });
|
||||
object.fire('selected', { e: e });
|
||||
this.requestRenderAll();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns currently active object
|
||||
* @return {fabric.Object} active object
|
||||
|
|
@ -1417,13 +1383,30 @@
|
|||
return this._activeObject;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an array with the current selected objects
|
||||
* @return {fabric.Object} active object
|
||||
*/
|
||||
getActiveObjects: function () {
|
||||
var active = this._activeObject;
|
||||
if (active) {
|
||||
if (active.type === 'activeSelection' && active._objects) {
|
||||
return active._objects;
|
||||
}
|
||||
else {
|
||||
return [active];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {fabric.Object} obj Object that was removed
|
||||
*/
|
||||
_onObjectRemoved: function(obj) {
|
||||
// removing active object should fire "selection:cleared" events
|
||||
if (this.getActiveObject() === obj) {
|
||||
if (obj === this._activeObject) {
|
||||
this.fire('before:selection:cleared', { target: obj });
|
||||
this._discardActiveObject();
|
||||
this.fire('selection:cleared', { target: obj });
|
||||
|
|
@ -1435,18 +1418,57 @@
|
|||
this.callSuper('_onObjectRemoved', obj);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets given object as the only active object on canvas
|
||||
* @param {fabric.Object} object Object to set as an active one
|
||||
* @param {Event} [e] Event (passed along when firing "object:selected")
|
||||
* @return {fabric.Canvas} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
setActiveObject: function (object, e) {
|
||||
var currentActiveObject = this._activeObject;
|
||||
if (object === currentActiveObject) {
|
||||
return this;
|
||||
}
|
||||
if (this._setActiveObject(object)) {
|
||||
currentActiveObject && currentActiveObject.fire('deselected', { e: e });
|
||||
this.fire('object:selected', { target: object, e: e });
|
||||
object.fire('selected', { e: e });
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} object
|
||||
*/
|
||||
_setActiveObject: function(object) {
|
||||
var active = this._activeObject;
|
||||
if (active === object) {
|
||||
return false;
|
||||
}
|
||||
if (this._discardActiveObject()) {
|
||||
this._activeObject = object;
|
||||
object.set('active', true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_discardActiveObject: function() {
|
||||
var obj = this._activeObject;
|
||||
if (obj) {
|
||||
obj.set('active', false);
|
||||
if (obj.onDeselect && typeof obj.onDeselect === 'function') {
|
||||
obj.onDeselect();
|
||||
if (obj && obj.onDeselect && typeof obj.onDeselect === 'function') {
|
||||
// onDeselect return TRUE to cancel selection;
|
||||
if (obj.onDeselect()) {
|
||||
return false;
|
||||
}
|
||||
obj.set('active', false);
|
||||
this._activeObject = null;
|
||||
}
|
||||
this._activeObject = null;
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -1462,121 +1484,14 @@
|
|||
var activeObject = this._activeObject;
|
||||
if (activeObject) {
|
||||
this.fire('before:selection:cleared', { target: activeObject, e: e });
|
||||
this._discardActiveObject();
|
||||
this.fire('selection:cleared', { e: e });
|
||||
activeObject.fire('deselected', { e: e });
|
||||
if (this._discardActiveObject()) {
|
||||
this.fire('selection:cleared', { e: e });
|
||||
activeObject.fire('deselected', { e: e });
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {fabric.Group} group
|
||||
*/
|
||||
_setActiveGroup: function(group) {
|
||||
this._activeGroup = group;
|
||||
if (group) {
|
||||
group.set('active', true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets active group to a specified one. If the function is called by fabric
|
||||
* as a consequence of a mouse event, the event is passed as a parmater and
|
||||
* sent to the fire function for the custom events. When used as a method the
|
||||
* e param does not have any application.
|
||||
* @param {fabric.Group} group Group to set as a current one
|
||||
* @param {Event} e Event object
|
||||
* @return {fabric.Canvas} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
setActiveGroup: function (group, e) {
|
||||
this._setActiveGroup(group);
|
||||
if (group) {
|
||||
this.fire('object:selected', { target: group, e: e });
|
||||
group.fire('selected', { e: e });
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns currently active group
|
||||
* @return {fabric.Group} Current group
|
||||
*/
|
||||
getActiveGroup: function () {
|
||||
return this._activeGroup;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_discardActiveGroup: function() {
|
||||
var g = this.getActiveGroup();
|
||||
if (g) {
|
||||
g.destroy();
|
||||
}
|
||||
this.setActiveGroup(null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Discards currently active group and fire events If the function is called by fabric
|
||||
* as a consequence of a mouse event, the event is passed as a parmater and
|
||||
* sent to the fire function for the custom events. When used as a method the
|
||||
* e param does not have any application.
|
||||
* @return {fabric.Canvas} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
discardActiveGroup: function (e) {
|
||||
var g = this.getActiveGroup();
|
||||
if (g) {
|
||||
this.fire('before:selection:cleared', { e: e, target: g });
|
||||
this._discardActiveGroup();
|
||||
this.fire('selection:cleared', { e: e });
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Deactivates all objects on canvas, removing any active group or object
|
||||
* @return {fabric.Canvas} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
deactivateAll: function () {
|
||||
var allObjects = this.getObjects(),
|
||||
i = 0,
|
||||
len = allObjects.length,
|
||||
obj;
|
||||
for ( ; i < len; i++) {
|
||||
obj = allObjects[i];
|
||||
obj && obj.set('active', false);
|
||||
}
|
||||
this._discardActiveGroup();
|
||||
this._discardActiveObject();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Deactivates all objects and dispatches appropriate events If the function is called by fabric
|
||||
* as a consequence of a mouse event, the event is passed as a parmater and
|
||||
* sent to the fire function for the custom events. When used as a method the
|
||||
* e param does not have any application.
|
||||
* @return {fabric.Canvas} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
deactivateAllWithDispatch: function (e) {
|
||||
var allObjects = this.getObjects(),
|
||||
i = 0,
|
||||
len = allObjects.length,
|
||||
obj;
|
||||
for ( ; i < len; i++) {
|
||||
obj = allObjects[i];
|
||||
obj && obj.set('active', false);
|
||||
}
|
||||
this.discardActiveGroup(e);
|
||||
this.discardActiveObject(e);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears a canvas element and removes all event listeners
|
||||
* @return {fabric.Canvas} thisArg
|
||||
|
|
@ -1602,7 +1517,7 @@
|
|||
* @chainable
|
||||
*/
|
||||
clear: function () {
|
||||
this.discardActiveGroup();
|
||||
// this.discardActiveGroup();
|
||||
this.discardActiveObject();
|
||||
this.clearContext(this.contextTop);
|
||||
return this.callSuper('clear');
|
||||
|
|
@ -1613,27 +1528,10 @@
|
|||
* @param {CanvasRenderingContext2D} ctx Context to render controls on
|
||||
*/
|
||||
drawControls: function(ctx) {
|
||||
var activeGroup = this.getActiveGroup();
|
||||
var activeObject = this._activeObject;
|
||||
|
||||
if (activeGroup) {
|
||||
activeGroup._renderControls(ctx);
|
||||
}
|
||||
else {
|
||||
this._drawObjectsControls(ctx);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_drawObjectsControls: function(ctx) {
|
||||
var object;
|
||||
for (var i = 0, len = this._objects.length; i < len; ++i) {
|
||||
object = this._objects[i];
|
||||
if (!object || !object.active) {
|
||||
continue;
|
||||
}
|
||||
object._renderControls(ctx);
|
||||
if (activeObject) {
|
||||
activeObject._renderControls(ctx);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -1659,14 +1557,14 @@
|
|||
* @returns the original values of instance which were changed
|
||||
*/
|
||||
_realizeGroupTransformOnObject: function(instance) {
|
||||
var layoutProps = ['angle', 'flipX', 'flipY', 'height', 'left', 'scaleX', 'scaleY', 'top', 'width'];
|
||||
if (instance.group && instance.group === this.getActiveGroup()) {
|
||||
if (instance.group && instance.group.type === 'activeSelection' && this._activeObject === instance.group) {
|
||||
var layoutProps = ['angle', 'flipX', 'flipY', 'left', 'scaleX', 'scaleY', 'skewX', 'skewY', 'top'];
|
||||
//Copy all the positionally relevant properties across now
|
||||
var originalValues = {};
|
||||
layoutProps.forEach(function(prop) {
|
||||
originalValues[prop] = instance[prop];
|
||||
});
|
||||
this.getActiveGroup().realizeTransform(instance);
|
||||
this._activeObject.realizeTransform(instance);
|
||||
return originalValues;
|
||||
}
|
||||
else {
|
||||
|
|
@ -1690,10 +1588,9 @@
|
|||
* @private
|
||||
*/
|
||||
_setSVGObject: function(markup, instance, reviver) {
|
||||
var originalProperties;
|
||||
//If the object is in a selection group, simulate what would happen to that
|
||||
//object when the group is deselected
|
||||
originalProperties = this._realizeGroupTransformOnObject(instance);
|
||||
var originalProperties = this._realizeGroupTransformOnObject(instance);
|
||||
this.callSuper('_setSVGObject', markup, instance, reviver);
|
||||
this._unwindGroupTransformOnObject(instance, originalProperties);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@
|
|||
* @param {Object} pointer
|
||||
*/
|
||||
_shouldRender: function(target, pointer) {
|
||||
var activeObject = this.getActiveGroup() || this.getActiveObject();
|
||||
var activeObject = this._activeObject;
|
||||
|
||||
if (activeObject && activeObject.isEditing && target === activeObject) {
|
||||
// if we mouse up/down over a editing textbox a cursor change,
|
||||
|
|
@ -533,16 +533,14 @@
|
|||
// save pointer for check in __onMouseUp event
|
||||
var pointer = this.getPointer(e, true);
|
||||
this._previousPointer = pointer;
|
||||
|
||||
var shouldRender = this._shouldRender(target, pointer),
|
||||
shouldGroup = this._shouldGroup(e, target);
|
||||
|
||||
if (this._shouldClearSelection(e, target)) {
|
||||
this.deactivateAllWithDispatch(e);
|
||||
this.discardActiveObject(e);
|
||||
}
|
||||
else if (shouldGroup) {
|
||||
this._handleGrouping(e, target);
|
||||
target = this.getActiveGroup();
|
||||
target = this._activeObject;
|
||||
}
|
||||
|
||||
if (this.selection && (!target || (!target.selectable && !target.isEditing))) {
|
||||
|
|
@ -559,13 +557,11 @@
|
|||
this._beforeTransform(e, target);
|
||||
this._setupCurrentTransform(e, target);
|
||||
}
|
||||
var activeObject = this.getActiveObject();
|
||||
if (target !== this.getActiveGroup() && target !== activeObject) {
|
||||
this.deactivateAll();
|
||||
if (target.selectable) {
|
||||
activeObject && activeObject.fire('deselected', { e: e });
|
||||
this.setActiveObject(target, e);
|
||||
}
|
||||
if (target.selectable) {
|
||||
this.setActiveObject(target, e);
|
||||
}
|
||||
else {
|
||||
this.discardActiveObject();
|
||||
}
|
||||
}
|
||||
this._handleEvent(e, 'down', target ? target : null);
|
||||
|
|
@ -806,10 +802,10 @@
|
|||
}
|
||||
|
||||
var hoverCursor = target.hoverCursor || this.hoverCursor,
|
||||
activeGroup = this.getActiveGroup(),
|
||||
activeSelection = this._activeObject && this._activeObject.type === 'activeSelection' ?
|
||||
this._activeObject : null,
|
||||
// only show proper corner when group selection is not active
|
||||
corner = target._findTargetCorner
|
||||
&& (!activeGroup || !activeGroup.contains(target))
|
||||
corner = (!activeSelection || !activeSelection.contains(target))
|
||||
&& target._findTargetCorner(this.getPointer(e, true));
|
||||
|
||||
if (!corner) {
|
||||
|
|
|
|||
|
|
@ -12,10 +12,9 @@
|
|||
* @return {Boolean}
|
||||
*/
|
||||
_shouldGroup: function(e, target) {
|
||||
var activeObject = this.getActiveObject();
|
||||
return e[this.selectionKey] && target && target.selectable &&
|
||||
(this.getActiveGroup() || (activeObject && activeObject !== target))
|
||||
&& this.selection;
|
||||
var activeObject = this._activeObject;
|
||||
return e[this.selectionKey] && target && target.selectable && this.selection &&
|
||||
(activeObject !== target || activeObject.type === 'activeSelection');
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -24,67 +23,52 @@
|
|||
* @param {fabric.Object} target
|
||||
*/
|
||||
_handleGrouping: function (e, target) {
|
||||
var activeGroup = this.getActiveGroup();
|
||||
|
||||
if (target === activeGroup) {
|
||||
var activeObject = this._activeObject;
|
||||
if (activeObject.__corner !== 0) {
|
||||
return;
|
||||
}
|
||||
if (target === activeObject) {
|
||||
// if it's a group, find target again, using activeGroup objects
|
||||
target = this.findTarget(e, true);
|
||||
// if even object is not found, bail out
|
||||
// if even object is not found or we are on activeObjectCorner, bail out
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (activeGroup) {
|
||||
this._updateActiveGroup(target, e);
|
||||
if (activeObject && activeObject.type === 'activeSelection') {
|
||||
this._updateActiveSelection(target, e);
|
||||
}
|
||||
else {
|
||||
this._createActiveGroup(target, e);
|
||||
this._createActiveSelection(target, e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateActiveGroup: function(target, e) {
|
||||
var activeGroup = this.getActiveGroup();
|
||||
|
||||
if (activeGroup.contains(target)) {
|
||||
|
||||
activeGroup.removeWithUpdate(target);
|
||||
target.set('active', false);
|
||||
|
||||
if (activeGroup.size() === 1) {
|
||||
// remove group alltogether if after removal it only contains 1 object
|
||||
this.discardActiveGroup(e);
|
||||
_updateActiveSelection: function(target, e) {
|
||||
var activeSelection = this._activeObject;
|
||||
if (activeSelection.contains(target)) {
|
||||
activeSelection.removeWithUpdate(target);
|
||||
if (activeSelection.size() === 1) {
|
||||
// activate last remaining object
|
||||
this.setActiveObject(activeGroup.item(0), e);
|
||||
this.setActiveObject(activeSelection.item(0), e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
activeGroup.addWithUpdate(target);
|
||||
activeSelection.addWithUpdate(target);
|
||||
}
|
||||
this.fire('selection:created', { target: activeGroup, e: e });
|
||||
activeGroup.set('active', true);
|
||||
this.fire('selection:created', { target: activeSelection, e: e });
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_createActiveGroup: function(target, e) {
|
||||
|
||||
if (this._activeObject && target !== this._activeObject) {
|
||||
|
||||
var group = this._createGroup(target);
|
||||
group.addWithUpdate();
|
||||
|
||||
this.setActiveGroup(group, e);
|
||||
this._activeObject = null;
|
||||
|
||||
this.fire('selection:created', { target: group, e: e });
|
||||
}
|
||||
|
||||
target.set('active', true);
|
||||
_createActiveSelection: function(target, e) {
|
||||
var group = this._createGroup(target);
|
||||
this.setActiveObject(group, e);
|
||||
this.fire('selection:created', { target: group, e: e });
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -92,14 +76,13 @@
|
|||
* @param {Object} target
|
||||
*/
|
||||
_createGroup: function(target) {
|
||||
|
||||
var objects = this.getObjects(),
|
||||
isActiveLower = objects.indexOf(this._activeObject) < objects.indexOf(target),
|
||||
groupObjects = isActiveLower
|
||||
? [this._activeObject, target]
|
||||
: [target, this._activeObject];
|
||||
this._activeObject.isEditing && this._activeObject.exitEditing();
|
||||
return new fabric.Group(groupObjects, {
|
||||
return new fabric.ActiveSelection(groupObjects, {
|
||||
canvas: this
|
||||
});
|
||||
},
|
||||
|
|
@ -117,11 +100,10 @@
|
|||
this.setActiveObject(group[0], e);
|
||||
}
|
||||
else if (group.length > 1) {
|
||||
group = new fabric.Group(group.reverse(), {
|
||||
group = new fabric.ActiveSelection(group.reverse(), {
|
||||
canvas: this
|
||||
});
|
||||
group.addWithUpdate();
|
||||
this.setActiveGroup(group, e);
|
||||
this.setActiveObject(group, e);
|
||||
this.fire('selection:created', { target: group, e: e });
|
||||
this.requestRenderAll();
|
||||
}
|
||||
|
|
@ -153,7 +135,6 @@
|
|||
currentObject.containsPoint(selectionX1Y1) ||
|
||||
currentObject.containsPoint(selectionX2Y2)
|
||||
) {
|
||||
currentObject.set('active', true);
|
||||
group.push(currentObject);
|
||||
|
||||
// only add one object if it's a click
|
||||
|
|
@ -173,14 +154,7 @@
|
|||
if (this.selection && this._groupSelector) {
|
||||
this._groupSelectedObjects(e);
|
||||
}
|
||||
|
||||
var activeGroup = this.getActiveGroup();
|
||||
if (activeGroup) {
|
||||
activeGroup.setObjectsCoords().setCoords();
|
||||
activeGroup.isMoving = false;
|
||||
this.setCursor(this.defaultCursor);
|
||||
}
|
||||
|
||||
this.setCursor(this.defaultCursor);
|
||||
// clear selection and current transformation
|
||||
this._groupSelector = null;
|
||||
this._currentTransform = null;
|
||||
|
|
|
|||
|
|
@ -467,25 +467,40 @@
|
|||
calcTransformMatrix: function(skipGroup) {
|
||||
var center = this.getCenterPoint(),
|
||||
translateMatrix = [1, 0, 0, 1, center.x, center.y],
|
||||
rotateMatrix = this._calcRotateMatrix(),
|
||||
rotateMatrix,
|
||||
dimensionMatrix = this._calcDimensionsTransformMatrix(this.skewX, this.skewY, true),
|
||||
matrix = this.group && !skipGroup ? this.group.calcTransformMatrix() : fabric.iMatrix.concat();
|
||||
matrix = multiplyMatrices(matrix, translateMatrix);
|
||||
matrix = multiplyMatrices(matrix, rotateMatrix);
|
||||
matrix;
|
||||
if (this.group && !skipGroup) {
|
||||
matrix = multiplyMatrices(this.group.calcTransformMatrix(), translateMatrix);
|
||||
}
|
||||
else {
|
||||
matrix = translateMatrix;
|
||||
}
|
||||
if (this.angle) {
|
||||
rotateMatrix = this._calcRotateMatrix();
|
||||
matrix = multiplyMatrices(matrix, rotateMatrix);
|
||||
}
|
||||
matrix = multiplyMatrices(matrix, dimensionMatrix);
|
||||
return matrix;
|
||||
},
|
||||
|
||||
_calcDimensionsTransformMatrix: function(skewX, skewY, flipping) {
|
||||
var skewMatrixX = [1, 0, Math.tan(degreesToRadians(skewX)), 1],
|
||||
skewMatrixY = [1, Math.tan(degreesToRadians(skewY)), 0, 1],
|
||||
var skewMatrix,
|
||||
scaleX = this.scaleX * (flipping && this.flipX ? -1 : 1),
|
||||
scaleY = this.scaleY * (flipping && this.flipY ? -1 : 1),
|
||||
scaleMatrix = [scaleX, 0, 0, scaleY],
|
||||
m = multiplyMatrices(scaleMatrix, skewMatrixX, true);
|
||||
return multiplyMatrices(m, skewMatrixY, true);
|
||||
scaleMatrix = [scaleX, 0, 0, scaleY, 0, 0];
|
||||
if (skewX) {
|
||||
skewMatrix = [1, 0, Math.tan(degreesToRadians(skewX)), 1];
|
||||
scaleMatrix = multiplyMatrices(scaleMatrix, skewMatrix, true);
|
||||
}
|
||||
if (skewY) {
|
||||
skewMatrix = [1, Math.tan(degreesToRadians(skewY)), 0, 1];
|
||||
scaleMatrix = multiplyMatrices(scaleMatrix, skewMatrix, true);
|
||||
}
|
||||
return scaleMatrix;
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* Calculate object dimensions from its properties
|
||||
* @private
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@
|
|||
* @return {String|Boolean} corner code (tl, tr, bl, br, etc.), or false if nothing is found
|
||||
*/
|
||||
_findTargetCorner: function(pointer) {
|
||||
if (!this.hasControls || !this.active) {
|
||||
// objects in group, anykind, are not self modificable,
|
||||
// must not return an hovered corner.
|
||||
if (!this.hasControls || !this.active || this.group) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +117,7 @@
|
|||
* @chainable
|
||||
*/
|
||||
drawSelectionBackground: function(ctx) {
|
||||
if (!this.selectionBackgroundColor || this.group || !this.active ||
|
||||
if (!this.selectionBackgroundColor || !this.active ||
|
||||
(this.canvas && !this.canvas.interactive)) {
|
||||
return this;
|
||||
}
|
||||
|
|
|
|||
193
src/shapes/active_selection.class.js
Normal file
193
src/shapes/active_selection.class.js
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
(function(global) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var fabric = global.fabric || (global.fabric = { });
|
||||
|
||||
if (fabric.ActiveSelection) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group class
|
||||
* @class fabric.ActiveSelection
|
||||
* @extends fabric.Group
|
||||
* @tutorial {@link http://fabricjs.com/fabric-intro-part-3#groups}
|
||||
* @see {@link fabric.ActiveSelection#initialize} for constructor definition
|
||||
*/
|
||||
fabric.ActiveSelection = fabric.util.createClass(fabric.Group, /** @lends fabric.ActiveSelection.prototype */ {
|
||||
|
||||
/**
|
||||
* Type of an object
|
||||
* @type String
|
||||
* @default
|
||||
*/
|
||||
type: 'activeSelection',
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} objects ActiveSelection objects
|
||||
* @param {Object} [options] Options object
|
||||
* @return {Object} thisArg
|
||||
*/
|
||||
initialize: function(objects, options) {
|
||||
options = options || { };
|
||||
|
||||
this._objects = objects || [];
|
||||
for (var i = this._objects.length; i--; ) {
|
||||
this._objects[i].group = this;
|
||||
}
|
||||
|
||||
if (options.originX) {
|
||||
this.originX = options.originX;
|
||||
}
|
||||
if (options.originY) {
|
||||
this.originY = options.originY;
|
||||
}
|
||||
this._calcBounds();
|
||||
this._updateObjectsCoords();
|
||||
fabric.Object.prototype.initialize.call(this, options);
|
||||
this.setCoords();
|
||||
},
|
||||
|
||||
/**
|
||||
* Change te activeSelection to a normal group,
|
||||
* High level function that automatically adds it to canvas as
|
||||
* active object. no events fired.
|
||||
* @since 2.0.0
|
||||
* @return {fabric.Group}
|
||||
*/
|
||||
toGroup: function() {
|
||||
var objects = this._objects;
|
||||
this._objects = [];
|
||||
var options = this.toObject();
|
||||
var newGroup = new fabric.Group([]);
|
||||
newGroup.set(options);
|
||||
newGroup.type = 'group';
|
||||
objects.forEach(function(object) {
|
||||
object.group = newGroup;
|
||||
object.canvas.remove(object);
|
||||
});
|
||||
newGroup._objects = objects;
|
||||
if (!this.canvas) {
|
||||
return newGroup;
|
||||
}
|
||||
var canvas = this.canvas;
|
||||
canvas._activeObject = newGroup;
|
||||
canvas.add(newGroup);
|
||||
newGroup.setCoords();
|
||||
return newGroup;
|
||||
},
|
||||
|
||||
/**
|
||||
* If returns true, deselection is cancelled.
|
||||
* @since 2.0.0
|
||||
* @return {Boolean} [cancel]
|
||||
*/
|
||||
onDeselect: function() {
|
||||
this.destroy();
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns string represenation of a group
|
||||
* @return {String}
|
||||
*/
|
||||
toString: function() {
|
||||
return '#<fabric.ActiveSelection: (' + this.complexity() + ')>';
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_set: function(key, value) {
|
||||
var i = this._objects.length;
|
||||
if (key === 'canvas') {
|
||||
while (i--) {
|
||||
this._objects[i].set(key, value);
|
||||
}
|
||||
}
|
||||
if (this.useSetOnGroup) {
|
||||
while (i--) {
|
||||
this._objects[i].setOnGroup(key, value);
|
||||
}
|
||||
}
|
||||
fabric.Object.prototype._set.call(this, key, value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Decide if the object should cache or not. Create its own cache level
|
||||
* objectCaching is a global flag, wins over everything
|
||||
* needsItsOwnCache should be used when the object drawing method requires
|
||||
* a cache step. None of the fabric classes requires it.
|
||||
* Generally you do not cache objects in groups because the group outside is cached.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
shouldCache: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if this object or a child object will cast a shadow
|
||||
* @return {Boolean}
|
||||
*/
|
||||
willDrawShadow: function() {
|
||||
if (this.shadow) {
|
||||
return this.callSuper('willDrawShadow');
|
||||
}
|
||||
for (var i = 0, len = this._objects.length; i < len; i++) {
|
||||
if (this._objects[i].willDrawShadow()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if this group or its parent group are caching, recursively up
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isOnACache: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders controls and borders for the object
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
* @param {Object} [styleOverride] properties to override the object style
|
||||
* @param {Object} [childrenOverride] properties to override the children overrides
|
||||
*/
|
||||
_renderControls: function(ctx, styleOverride, childrenOverride) {
|
||||
ctx.save();
|
||||
ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
|
||||
this.callSuper('_renderControls', ctx, styleOverride);
|
||||
childrenOverride = childrenOverride || { };
|
||||
if (typeof childrenOverride.hasControls === 'undefined') {
|
||||
childrenOverride.hasControls = false;
|
||||
}
|
||||
if (typeof childrenOverride.hasRotatingPoint === 'undefined') {
|
||||
childrenOverride.hasRotatingPoint = false;
|
||||
}
|
||||
childrenOverride.forActiveSelection = true;
|
||||
for (var i = 0, len = this._objects.length; i < len; i++) {
|
||||
this._objects[i]._renderControls(ctx, childrenOverride);
|
||||
}
|
||||
ctx.restore();
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns {@link fabric.ActiveSelection} instance from an object representation
|
||||
* @static
|
||||
* @memberOf fabric.ActiveSelection
|
||||
* @param {Object} object Object to create a group from
|
||||
* @param {Function} [callback] Callback to invoke when an ActiveSelection instance is created
|
||||
*/
|
||||
fabric.ActiveSelection.fromObject = function(object, callback) {
|
||||
fabric.util.enlivenObjects(object.objects, function(enlivenedObjects) {
|
||||
delete object.objects;
|
||||
callback && callback(new fabric.ActiveSelection(enlivenedObjects, object, true));
|
||||
});
|
||||
};
|
||||
|
||||
})(typeof exports !== 'undefined' ? exports : this);
|
||||
|
|
@ -86,17 +86,7 @@
|
|||
this.originY = options.originY;
|
||||
}
|
||||
|
||||
if (isAlreadyGrouped) {
|
||||
// do not change coordinate of objects enclosed in a group,
|
||||
// because objects coordinate system have been group coodinate system already.
|
||||
var object;
|
||||
for (var i = this._objects.length; i--; ) {
|
||||
object = this._objects[i];
|
||||
object.__origHasControls = object.hasControls;
|
||||
object.hasControls = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!isAlreadyGrouped) {
|
||||
var center = options && options.centerPoint;
|
||||
// if coming from svg i do not want to calc bounds.
|
||||
// i assume width and height are passed along options
|
||||
|
|
@ -126,10 +116,6 @@
|
|||
* @param {fabric.Point} center, current center of group.
|
||||
*/
|
||||
_updateObjectCoords: function(object, center) {
|
||||
// do not display corners of objects enclosed in a group
|
||||
object.__origHasControls = object.hasControls;
|
||||
object.hasControls = false;
|
||||
|
||||
var objectLeft = object.left,
|
||||
objectTop = object.top,
|
||||
ignoreZoom = true, skipAbsolute = true;
|
||||
|
|
@ -138,6 +124,7 @@
|
|||
left: objectLeft - center.x,
|
||||
top: objectTop - center.y
|
||||
});
|
||||
object.group = this;
|
||||
object.setCoords(ignoreZoom, skipAbsolute);
|
||||
},
|
||||
|
||||
|
|
@ -163,8 +150,6 @@
|
|||
object.group = this;
|
||||
object._set('canvas', this.canvas);
|
||||
}
|
||||
// since _restoreObjectsState set objects inactive
|
||||
this.forEachObject(this._setObjectActive, this);
|
||||
this._calcBounds();
|
||||
this._updateObjectsCoords();
|
||||
this.setCoords();
|
||||
|
|
@ -172,14 +157,6 @@
|
|||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_setObjectActive: function(object) {
|
||||
object.set('active', true);
|
||||
object.group = this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes an object from a group; Then recalculates group's dimension, position.
|
||||
* @param {Object} object
|
||||
|
|
@ -189,8 +166,6 @@
|
|||
removeWithUpdate: function(object) {
|
||||
this._restoreObjectsState();
|
||||
fabric.util.resetObjectTransform(this);
|
||||
// since _restoreObjectsState set objects inactive
|
||||
this.forEachObject(this._setObjectActive, this);
|
||||
|
||||
this.remove(object);
|
||||
this._calcBounds();
|
||||
|
|
@ -206,7 +181,6 @@
|
|||
_onObjectAdded: function(object) {
|
||||
this.dirty = true;
|
||||
object.group = this;
|
||||
object._set('canvas', this.canvas);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -215,7 +189,6 @@
|
|||
_onObjectRemoved: function(object) {
|
||||
this.dirty = true;
|
||||
delete object.group;
|
||||
object.set('active', false);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -223,18 +196,11 @@
|
|||
*/
|
||||
_set: function(key, value) {
|
||||
var i = this._objects.length;
|
||||
|
||||
if (key === 'canvas') {
|
||||
while (i--) {
|
||||
this._objects[i].set(key, value);
|
||||
}
|
||||
}
|
||||
if (this.useSetOnGroup) {
|
||||
while (i--) {
|
||||
this._objects[i].setOnGroup(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
this.callSuper('_set', key, value);
|
||||
},
|
||||
|
||||
|
|
@ -342,7 +308,7 @@
|
|||
*/
|
||||
drawObject: function(ctx) {
|
||||
for (var i = 0, len = this._objects.length; i < len; i++) {
|
||||
this._renderObject(this._objects[i], ctx);
|
||||
this._objects[i].render(ctx);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -369,34 +335,6 @@
|
|||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders controls and borders for the object
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
* @param {Object} [styleOverride] properties to override the object style
|
||||
* @param {Object} [childrenOverride] properties to override the children overrides
|
||||
*/
|
||||
_renderControls: function(ctx, styleOverride, childrenOverride) {
|
||||
ctx.save();
|
||||
ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
|
||||
this.callSuper('_renderControls', ctx, styleOverride);
|
||||
if (this.canvas && this === this.canvas.getActiveGroup()) {
|
||||
for (var i = 0, len = this._objects.length; i < len; i++) {
|
||||
this._objects[i]._renderControls(ctx, childrenOverride);
|
||||
}
|
||||
}
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_renderObject: function(object, ctx) {
|
||||
var originalHasRotatingPoint = object.hasRotatingPoint;
|
||||
object.hasRotatingPoint = false;
|
||||
object.render(ctx);
|
||||
object.hasRotatingPoint = originalHasRotatingPoint;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retores original state of each of group objects (original state is that which was before group was created).
|
||||
* @private
|
||||
|
|
@ -440,11 +378,7 @@
|
|||
_restoreObjectState: function(object) {
|
||||
this.realizeTransform(object);
|
||||
object.setCoords();
|
||||
object.hasControls = object.__origHasControls;
|
||||
delete object.__origHasControls;
|
||||
object.set('active', false);
|
||||
delete object.group;
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
|
@ -457,6 +391,43 @@
|
|||
return this._restoreObjectsState();
|
||||
},
|
||||
|
||||
/**
|
||||
* make a group an active selection, remove the group from canvas
|
||||
* the group has to be on canvas for this to work.
|
||||
* @return {fabric.ActiveSelection} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
toActiveSelection: function() {
|
||||
if (!this.canvas) {
|
||||
return;
|
||||
}
|
||||
var objects = this._objects, canvas = this.canvas;
|
||||
this._objects = [];
|
||||
var options = this.toObject();
|
||||
var activeSelection = new fabric.ActiveSelection([]);
|
||||
activeSelection.set(options);
|
||||
activeSelection.type = 'activeSelection';
|
||||
canvas.remove(this);
|
||||
objects.forEach(function(object) {
|
||||
object.group = activeSelection;
|
||||
object.dirty = true;
|
||||
canvas.add(object);
|
||||
});
|
||||
activeSelection._objects = objects;
|
||||
canvas._activeObject = activeSelection;
|
||||
activeSelection.setCoords();
|
||||
return activeSelection;
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroys a group (restoring state of its objects)
|
||||
* @return {fabric.Group} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
ungroupOnCanvas: function() {
|
||||
return this._restoreObjectsState();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets coordinates of all objects inside group
|
||||
* @return {fabric.Group} thisArg
|
||||
|
|
|
|||
|
|
@ -784,7 +784,6 @@
|
|||
};
|
||||
|
||||
fabric.util.populateWithProperties(this, object, propertiesToInclude);
|
||||
|
||||
if (!this.includeDefaultValues) {
|
||||
object = this._removeDefaultValues(object);
|
||||
}
|
||||
|
|
@ -1163,7 +1162,7 @@
|
|||
if (!this.group) {
|
||||
ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
|
||||
}
|
||||
if (this.group && this.group === this.canvas.getActiveGroup()) {
|
||||
if (styleOverride.forActiveSelection) {
|
||||
ctx.rotate(degreesToRadians(options.angle));
|
||||
drawBorders && this.drawBordersInGroup(ctx, options, styleOverride);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -671,14 +671,14 @@
|
|||
* @chainable true
|
||||
*/
|
||||
setViewportTransform: function (vpt) {
|
||||
var activeGroup = this._activeGroup, object, ignoreVpt = false, skipAbsolute = true;
|
||||
var activeObject = this._activeObject, object, ignoreVpt = false, skipAbsolute = true;
|
||||
this.viewportTransform = vpt;
|
||||
for (var i = 0, len = this._objects.length; i < len; i++) {
|
||||
object = this._objects[i];
|
||||
object.group || object.setCoords(ignoreVpt, skipAbsolute);
|
||||
}
|
||||
if (activeGroup) {
|
||||
activeGroup.setCoords(ignoreVpt, skipAbsolute);
|
||||
if (activeObject && activeObject.type === 'activeSelection') {
|
||||
activeObject.setCoords(ignoreVpt, skipAbsolute);
|
||||
}
|
||||
this.calcViewportBoundaries();
|
||||
this.renderOnAddRemove && this.requestRenderAll();
|
||||
|
|
@ -1376,7 +1376,6 @@
|
|||
},
|
||||
|
||||
/**
|
||||
* push single object svg representation in the markup
|
||||
* @private
|
||||
*/
|
||||
_setSVGObject: function(markup, instance, reviver) {
|
||||
|
|
@ -1440,10 +1439,10 @@
|
|||
if (!object) {
|
||||
return this;
|
||||
}
|
||||
var activeGroup = this._activeGroup,
|
||||
var activeSelection = this._activeObject,
|
||||
i, obj, objs;
|
||||
if (object === activeGroup) {
|
||||
objs = activeGroup._objects;
|
||||
if (object === activeSelection && object.type === 'activeSelection') {
|
||||
objs = activeSelection._objects;
|
||||
for (i = objs.length; i--;) {
|
||||
obj = objs[i];
|
||||
removeFromArray(this._objects, obj);
|
||||
|
|
@ -1469,10 +1468,10 @@
|
|||
if (!object) {
|
||||
return this;
|
||||
}
|
||||
var activeGroup = this._activeGroup,
|
||||
var activeSelection = this._activeObject,
|
||||
i, obj, objs;
|
||||
if (object === activeGroup) {
|
||||
objs = activeGroup._objects;
|
||||
if (object === activeSelection && object.type === 'activeSelection') {
|
||||
objs = activeSelection._objects;
|
||||
for (i = 0; i < objs.length; i++) {
|
||||
obj = objs[i];
|
||||
removeFromArray(this._objects, obj);
|
||||
|
|
@ -1498,11 +1497,11 @@
|
|||
if (!object) {
|
||||
return this;
|
||||
}
|
||||
var activeGroup = this._activeGroup,
|
||||
var activeSelection = this._activeObject,
|
||||
i, obj, idx, newIdx, objs;
|
||||
|
||||
if (object === activeGroup) {
|
||||
objs = activeGroup._objects;
|
||||
if (object === activeSelection && object.type === 'activeSelection') {
|
||||
objs = activeSelection._objects;
|
||||
for (i = 0; i < objs.length; i++) {
|
||||
obj = objs[i];
|
||||
idx = this._objects.indexOf(obj);
|
||||
|
|
@ -1566,11 +1565,11 @@
|
|||
if (!object) {
|
||||
return this;
|
||||
}
|
||||
var activeGroup = this._activeGroup,
|
||||
var activeSelection = this._activeObject,
|
||||
i, obj, idx, newIdx, objs;
|
||||
|
||||
if (object === activeGroup) {
|
||||
objs = activeGroup._objects;
|
||||
if (object === activeSelection && object.type === 'activeSelection') {
|
||||
objs = activeSelection._objects;
|
||||
for (i = objs.length; i--;) {
|
||||
obj = objs[i];
|
||||
idx = this._objects.indexOf(obj);
|
||||
|
|
|
|||
1
test.js
1
test.js
|
|
@ -12,6 +12,7 @@ testrunner.run({
|
|||
deps: "./test/fixtures/test_script.js",
|
||||
code: "./dist/fabric.js",
|
||||
tests: [
|
||||
'./test/unit/activeselection.js',
|
||||
'./test/unit/animation.js',
|
||||
'./test/unit/rect.js',
|
||||
'./test/unit/ellipse.js',
|
||||
|
|
|
|||
235
test/unit/activeselection.js
Normal file
235
test/unit/activeselection.js
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
(function() {
|
||||
|
||||
var el = fabric.document.createElement('canvas');
|
||||
el.width = 600; el.height = 600;
|
||||
|
||||
var canvas = this.canvas = fabric.isLikelyNode ? fabric.createCanvasForNode(600, 600, {enableRetinaScaling: false}) : new fabric.Canvas(el, {enableRetinaScaling: false});
|
||||
|
||||
function makeAsWith2Objects() {
|
||||
var rect1 = new fabric.Rect({ top: 100, left: 100, width: 30, height: 10, strokeWidth: 0 }),
|
||||
rect2 = new fabric.Rect({ top: 120, left: 50, width: 10, height: 40, strokeWidth: 0 });
|
||||
|
||||
return new fabric.ActiveSelection([rect1, rect2], {strokeWidth: 0});
|
||||
}
|
||||
|
||||
function makeAsWith2ObjectsWithOpacity() {
|
||||
var rect1 = new fabric.Rect({ top: 100, left: 100, width: 30, height: 10, strokeWidth: 0, opacity: 0.5 }),
|
||||
rect2 = new fabric.Rect({ top: 120, left: 50, width: 10, height: 40, strokeWidth: 0, opacity: 0.8 });
|
||||
|
||||
return new fabric.ActiveSelection([rect1, rect2], {strokeWidth: 0});
|
||||
}
|
||||
|
||||
function makeAsWith4Objects() {
|
||||
var rect1 = new fabric.Rect({ top: 100, left: 100, width: 30, height: 10 }),
|
||||
rect2 = new fabric.Rect({ top: 120, left: 50, width: 10, height: 40 }),
|
||||
rect3 = new fabric.Rect({ top: 40, left: 0, width: 20, height: 40 }),
|
||||
rect4 = new fabric.Rect({ top: 75, left: 75, width: 40, height: 40 });
|
||||
|
||||
return new fabric.ActiveSelection([rect1, rect2, rect3, rect4]);
|
||||
}
|
||||
|
||||
QUnit.module('fabric.ActiveSelection', {
|
||||
teardown: function() {
|
||||
canvas.clear();
|
||||
canvas.backgroundColor = fabric.Canvas.prototype.backgroundColor;
|
||||
canvas.calcOffset();
|
||||
}
|
||||
});
|
||||
|
||||
test('constructor', function() {
|
||||
var group = makeAsWith2Objects();
|
||||
|
||||
ok(group);
|
||||
ok(group instanceof fabric.ActiveSelection, 'should be instance of fabric.ActiveSelection');
|
||||
});
|
||||
|
||||
test('toString', function() {
|
||||
var group = makeAsWith2Objects();
|
||||
equal(group.toString(), '#<fabric.ActiveSelection: (2)>', 'should return proper representation');
|
||||
});
|
||||
|
||||
test('toObject', function() {
|
||||
var group = makeAsWith2Objects();
|
||||
|
||||
ok(typeof group.toObject === 'function');
|
||||
|
||||
var clone = group.toObject();
|
||||
|
||||
var expectedObject = {
|
||||
'type': 'activeSelection',
|
||||
'originX': 'left',
|
||||
'originY': 'top',
|
||||
'left': 50,
|
||||
'top': 100,
|
||||
'width': 80,
|
||||
'height': 60,
|
||||
'fill': 'rgb(0,0,0)',
|
||||
'stroke': null,
|
||||
'strokeWidth': 0,
|
||||
'strokeDashArray': null,
|
||||
'strokeLineCap': 'butt',
|
||||
'strokeLineJoin': 'miter',
|
||||
'strokeMiterLimit': 10,
|
||||
'scaleX': 1,
|
||||
'scaleY': 1,
|
||||
'shadow': null,
|
||||
'visible': true,
|
||||
'backgroundColor': '',
|
||||
'clipTo': null,
|
||||
'angle': 0,
|
||||
'flipX': false,
|
||||
'flipY': false,
|
||||
'opacity': 1,
|
||||
'fillRule': 'nonzero',
|
||||
'globalCompositeOperation': 'source-over',
|
||||
'transformMatrix': null,
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'objects': clone.objects
|
||||
};
|
||||
|
||||
deepEqual(clone, expectedObject);
|
||||
|
||||
ok(group !== clone, 'should produce different object');
|
||||
ok(group.getObjects() !== clone.objects, 'should produce different object array');
|
||||
ok(group.getObjects()[0] !== clone.objects[0], 'should produce different objects in array');
|
||||
});
|
||||
|
||||
test('toObject without default values', function() {
|
||||
var group = makeAsWith2Objects();
|
||||
group.includeDefaultValues = false;
|
||||
var clone = group.toObject();
|
||||
var objects = [{
|
||||
type: 'rect',
|
||||
left: 10,
|
||||
top: -30,
|
||||
width: 30,
|
||||
height: 10,
|
||||
strokeWidth: 0
|
||||
}, {
|
||||
type: 'rect',
|
||||
left: -40,
|
||||
top: -10,
|
||||
width: 10,
|
||||
height: 40,
|
||||
strokeWidth: 0
|
||||
}];
|
||||
var expectedObject = {
|
||||
'type': 'activeSelection',
|
||||
'left': 50,
|
||||
'top': 100,
|
||||
'width': 80,
|
||||
'height': 60,
|
||||
'objects': objects
|
||||
};
|
||||
deepEqual(clone, expectedObject);
|
||||
});
|
||||
|
||||
test('_renderControls', function() {
|
||||
ok(typeof fabric.ActiveSelection.prototype._renderControls === 'function');
|
||||
});
|
||||
|
||||
asyncTest('fromObject', function() {
|
||||
var group = makeAsWith2ObjectsWithOpacity();
|
||||
|
||||
ok(typeof fabric.ActiveSelection.fromObject === 'function');
|
||||
var groupObject = group.toObject();
|
||||
|
||||
fabric.ActiveSelection.fromObject(groupObject, function(newGroupFromObject) {
|
||||
|
||||
var objectFromOldGroup = group.toObject();
|
||||
var objectFromNewGroup = newGroupFromObject.toObject();
|
||||
|
||||
ok(newGroupFromObject instanceof fabric.ActiveSelection);
|
||||
|
||||
deepEqual(objectFromOldGroup.objects[0], objectFromNewGroup.objects[0]);
|
||||
deepEqual(objectFromOldGroup.objects[1], objectFromNewGroup.objects[1]);
|
||||
|
||||
// delete `objects` arrays, since `assertHashEqual` fails to compare them for equality
|
||||
delete objectFromOldGroup.objects;
|
||||
delete objectFromNewGroup.objects;
|
||||
|
||||
deepEqual(objectFromOldGroup, objectFromNewGroup);
|
||||
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
test('get with locked objects', function() {
|
||||
var group = makeAsWith2Objects();
|
||||
|
||||
equal(group.get('lockMovementX'), false);
|
||||
|
||||
// TODO acitveGroup
|
||||
// group.getObjects()[0].lockMovementX = true;
|
||||
// equal(group.get('lockMovementX'), true);
|
||||
//
|
||||
// group.getObjects()[0].lockMovementX = false;
|
||||
// equal(group.get('lockMovementX'), false);
|
||||
|
||||
group.set('lockMovementX', true);
|
||||
equal(group.get('lockMovementX'), true);
|
||||
|
||||
// group.set('lockMovementX', false);
|
||||
// group.getObjects()[0].lockMovementY = true;
|
||||
// group.getObjects()[1].lockRotation = true;
|
||||
//
|
||||
// equal(group.get('lockMovementY'), true);
|
||||
// equal(group.get('lockRotation'), true);
|
||||
});
|
||||
|
||||
test('insertAt', function() {
|
||||
var rect1 = new fabric.Rect(),
|
||||
rect2 = new fabric.Rect(),
|
||||
group = new fabric.Group();
|
||||
|
||||
group.add(rect1, rect2);
|
||||
|
||||
ok(typeof group.insertAt == 'function', 'should respond to `insertAt` method');
|
||||
|
||||
group.insertAt(rect1, 1);
|
||||
equal(group.item(1), rect1);
|
||||
group.insertAt(rect2, 2);
|
||||
equal(group.item(2), rect2);
|
||||
equal(group.insertAt(rect1, 2), group, 'should be chainable');
|
||||
});
|
||||
|
||||
test('group addWithUpdate', function() {
|
||||
var rect1 = new fabric.Rect({ top: 1, left: 1, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}),
|
||||
rect2 = new fabric.Rect({ top: 5, left: 5, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}),
|
||||
group = new fabric.Group([rect1]);
|
||||
|
||||
var coords = group.oCoords;
|
||||
group.addWithUpdate(rect2);
|
||||
var newCoords = group.oCoords;
|
||||
notEqual(coords, newCoords, 'object coords have been recalculated - add');
|
||||
});
|
||||
|
||||
test('group removeWithUpdate', function() {
|
||||
var rect1 = new fabric.Rect({ top: 1, left: 1, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}),
|
||||
rect2 = new fabric.Rect({ top: 5, left: 5, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}),
|
||||
group = new fabric.Group([rect1, rect2]);
|
||||
|
||||
var coords = group.oCoords;
|
||||
group.removeWithUpdate(rect2);
|
||||
var newCoords = group.oCoords;
|
||||
notEqual(coords, newCoords, 'object coords have been recalculated - remove');
|
||||
});
|
||||
|
||||
test('ActiveSelection shouldCache', function() {
|
||||
var rect1 = new fabric.Rect({ top: 1, left: 1, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: true}),
|
||||
rect2 = new fabric.Rect({ top: 5, left: 5, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: true}),
|
||||
group = new fabric.ActiveSelection([rect1, rect2], { objectCaching: true});
|
||||
|
||||
equal(group.shouldCache(), false, 'Active selection do not cache');
|
||||
});
|
||||
|
||||
test('canvas property propagation', function() {
|
||||
var g2 = makeAsWith4Objects();
|
||||
|
||||
canvas.add(g2);
|
||||
equal(g2.canvas, canvas);
|
||||
equal(g2._objects[3].canvas, canvas);
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
@ -105,7 +105,6 @@
|
|||
},
|
||||
teardown: function() {
|
||||
canvas.clear();
|
||||
canvas.setActiveGroup(null);
|
||||
canvas.backgroundColor = fabric.Canvas.prototype.backgroundColor;
|
||||
canvas.overlayColor = fabric.Canvas.prototype.overlayColor;
|
||||
canvas.calcOffset();
|
||||
|
|
@ -495,8 +494,8 @@
|
|||
canvas.add(rect1);
|
||||
canvas.add(rect2);
|
||||
canvas.add(rect3);
|
||||
var group = new fabric.Group([rect1, rect2]);
|
||||
canvas.setActiveGroup(group);
|
||||
var group = new fabric.ActiveSelection([rect1, rect2]);
|
||||
canvas.setActiveObject(group);
|
||||
target = canvas.findTarget({
|
||||
clientX: 5, clientY: 5
|
||||
});
|
||||
|
|
@ -526,8 +525,8 @@
|
|||
canvas.preserveObjectStacking = true;
|
||||
canvas.add(rect1);
|
||||
canvas.add(rect2);
|
||||
var group = new fabric.Group([rect1, rect2]);
|
||||
canvas.setActiveGroup(group);
|
||||
var group = new fabric.ActiveSelection([rect1, rect2]);
|
||||
canvas.setActiveObject(group);
|
||||
target = canvas.findTarget({
|
||||
clientX: 8, clientY: 8
|
||||
});
|
||||
|
|
@ -541,7 +540,7 @@
|
|||
canvas.preserveObjectStacking = false;
|
||||
});
|
||||
|
||||
test('activeGroup sendToBack', function() {
|
||||
test('ActiveSelection sendToBack', function() {
|
||||
|
||||
var rect1 = makeRect(),
|
||||
rect2 = makeRect(),
|
||||
|
|
@ -550,11 +549,11 @@
|
|||
|
||||
canvas.add(rect1, rect2, rect3, rect4);
|
||||
|
||||
var group = new fabric.Group([rect3, rect4]);
|
||||
canvas.setActiveGroup(group);
|
||||
var activeSel = new fabric.ActiveSelection([rect3, rect4]);
|
||||
canvas.setActiveObject(activeSel);
|
||||
equal(canvas._objects[0], rect1, 'rect1 should be last');
|
||||
equal(canvas._objects[1], rect2, 'rect2 should be second');
|
||||
canvas.sendToBack(group);
|
||||
canvas.sendToBack(activeSel);
|
||||
equal(canvas._objects[0], rect3, 'rect3 should be the new last');
|
||||
equal(canvas._objects[1], rect4, 'rect3 should be the new second');
|
||||
equal(canvas._objects[2], rect1, 'rect1 should be the third object');
|
||||
|
|
@ -570,11 +569,11 @@
|
|||
|
||||
canvas.add(rect1, rect2, rect3, rect4);
|
||||
|
||||
var group = new fabric.Group([rect1, rect2]);
|
||||
canvas.setActiveGroup(group);
|
||||
var activeSel = new fabric.ActiveSelection([rect1, rect2]);
|
||||
canvas.setActiveObject(activeSel);
|
||||
equal(canvas._objects[0], rect1, 'rect1 should be last');
|
||||
equal(canvas._objects[1], rect2, 'rect2 should be second');
|
||||
canvas.bringToFront(group);
|
||||
canvas.bringToFront(activeSel);
|
||||
equal(canvas._objects[0], rect3, 'rect3 should be the new last');
|
||||
equal(canvas._objects[1], rect4, 'rect3 should be the new second');
|
||||
equal(canvas._objects[2], rect1, 'rect1 should be the third object');
|
||||
|
|
@ -590,11 +589,11 @@
|
|||
|
||||
canvas.add(rect1, rect2, rect3, rect4);
|
||||
|
||||
var group = new fabric.Group([rect1, rect2]);
|
||||
canvas.setActiveGroup(group);
|
||||
var activeSel = new fabric.ActiveSelection([rect1, rect2]);
|
||||
canvas.setActiveObject(activeSel);
|
||||
equal(canvas._objects[0], rect1, 'rect1 should be last');
|
||||
equal(canvas._objects[1], rect2, 'rect2 should be second');
|
||||
canvas.bringForward(group);
|
||||
canvas.bringForward(activeSel);
|
||||
equal(canvas._objects[0], rect3, 'rect3 should be the new last');
|
||||
equal(canvas._objects[1], rect1, 'rect1 should be the new second');
|
||||
equal(canvas._objects[2], rect2, 'rect2 should be the third object');
|
||||
|
|
@ -609,11 +608,11 @@
|
|||
|
||||
canvas.add(rect1, rect2, rect3, rect4);
|
||||
|
||||
var group = new fabric.Group([rect3, rect4]);
|
||||
canvas.setActiveGroup(group);
|
||||
var activeSel = new fabric.ActiveSelection([rect3, rect4]);
|
||||
canvas.setActiveObject(activeSel);
|
||||
equal(canvas._objects[0], rect1, 'rect1 should be last');
|
||||
equal(canvas._objects[1], rect2, 'rect2 should be second');
|
||||
canvas.sendBackwards(group);
|
||||
canvas.sendBackwards(activeSel);
|
||||
equal(canvas._objects[0], rect1, 'rect1 is still last');
|
||||
equal(canvas._objects[1], rect3, 'rect3 should be shifted down by 1');
|
||||
equal(canvas._objects[2], rect4, 'rect4 should be shifted down by 1');
|
||||
|
|
@ -718,7 +717,7 @@
|
|||
canvas.add(rect, circle);
|
||||
var json = JSON.stringify(canvas);
|
||||
|
||||
canvas.setActiveGroup(new fabric.Group([rect, circle])).renderAll();
|
||||
canvas.setActiveObject(new fabric.ActiveSelection([rect, circle], { canvas: canvas }));
|
||||
var jsonWithActiveGroup = JSON.stringify(canvas);
|
||||
|
||||
equal(json, jsonWithActiveGroup);
|
||||
|
|
@ -979,7 +978,7 @@
|
|||
canvas.add(group);
|
||||
canvas.renderAll();
|
||||
|
||||
canvas.deactivateAll();
|
||||
canvas._discardActiveObject();
|
||||
var json = JSON.stringify( canvas.toDatalessJSON() );
|
||||
canvas.clear();
|
||||
canvas.loadFromDatalessJSON(json, function() {
|
||||
|
|
@ -1226,7 +1225,7 @@
|
|||
|
||||
test('getActiveObject', function() {
|
||||
ok(typeof canvas.getActiveObject == 'function');
|
||||
|
||||
equal(canvas.getActiveObject(), null, 'should initially be null');
|
||||
var rect1 = makeRect(),
|
||||
rect2 = makeRect();
|
||||
|
||||
|
|
@ -1239,19 +1238,16 @@
|
|||
equal(canvas.getActiveObject(), rect2);
|
||||
});
|
||||
|
||||
test('getSetActiveGroup', function() {
|
||||
ok(typeof canvas.getActiveGroup == 'function');
|
||||
ok(typeof canvas.setActiveGroup == 'function');
|
||||
|
||||
equal(canvas.getActiveGroup(), null, 'should initially be null');
|
||||
test('getsetActiveObject', function() {
|
||||
equal(canvas.getActiveObject(), null, 'should initially be null');
|
||||
|
||||
var group = new fabric.Group([
|
||||
makeRect({ left: 10, top: 10 }),
|
||||
makeRect({ left: 20, top: 20 })
|
||||
]);
|
||||
|
||||
equal(canvas.setActiveGroup(group), canvas, 'should be chainable');
|
||||
equal(canvas.getActiveGroup(), group);
|
||||
equal(canvas.setActiveObject(group), canvas, 'should be chainable');
|
||||
equal(canvas.getActiveObject(), group);
|
||||
});
|
||||
|
||||
test('item', function() {
|
||||
|
|
@ -1270,28 +1266,25 @@
|
|||
equal(canvas.item(0), rect2);
|
||||
});
|
||||
|
||||
test('discardActiveGroup', function() {
|
||||
ok(typeof canvas.discardActiveGroup == 'function');
|
||||
var group = new fabric.Group([makeRect(), makeRect()]);
|
||||
canvas.setActiveGroup(group);
|
||||
equal(canvas.discardActiveGroup(), canvas, 'should be chainable');
|
||||
equal(canvas.getActiveGroup(), null, 'removing active group sets it to null');
|
||||
test('discardActiveObject on ActiveSelection', function() {
|
||||
var group = new fabric.ActiveSelection([makeRect(), makeRect()]);
|
||||
canvas.setActiveObject(group);
|
||||
equal(canvas.discardActiveObject(), canvas, 'should be chainable');
|
||||
equal(canvas.getActiveObject(), null, 'removing active group sets it to null');
|
||||
});
|
||||
|
||||
test('deactivateAll', function() {
|
||||
ok(typeof canvas.deactivateAll == 'function');
|
||||
test('_discardActiveObject', function() {
|
||||
|
||||
canvas.add(makeRect());
|
||||
canvas.setActiveObject(canvas.item(0));
|
||||
|
||||
canvas.deactivateAll();
|
||||
canvas._discardActiveObject();
|
||||
ok(!canvas.item(0).active);
|
||||
equal(canvas.getActiveObject(), null);
|
||||
equal(canvas.getActiveGroup(), null);
|
||||
});
|
||||
|
||||
test('deactivateAllWithDispatch', function() {
|
||||
ok(typeof canvas.deactivateAllWithDispatch == 'function');
|
||||
test('discardActiveObject', function() {
|
||||
ok(typeof canvas.discardActiveObject == 'function');
|
||||
|
||||
canvas.add(makeRect());
|
||||
canvas.setActiveObject(canvas.item(0));
|
||||
|
|
@ -1301,7 +1294,7 @@
|
|||
makeRect({ left: 20, top: 20 })
|
||||
]);
|
||||
|
||||
canvas.setActiveGroup(group);
|
||||
canvas.setActiveObject(group);
|
||||
|
||||
var eventsFired = {
|
||||
selectionCleared: false
|
||||
|
|
@ -1311,10 +1304,10 @@
|
|||
eventsFired.selectionCleared = true;
|
||||
});
|
||||
|
||||
canvas.deactivateAllWithDispatch();
|
||||
canvas.discardActiveObject();
|
||||
ok(!canvas.item(0).active);
|
||||
equal(canvas.getActiveObject(), null);
|
||||
equal(canvas.getActiveGroup(), null);
|
||||
equal(canvas.getActiveObject(), null);
|
||||
|
||||
for (var prop in eventsFired) {
|
||||
ok(eventsFired[prop]);
|
||||
|
|
@ -1347,7 +1340,7 @@
|
|||
canvas.add(rect, circle);
|
||||
var svg = canvas.toSVG();
|
||||
|
||||
canvas.setActiveGroup(new fabric.Group([rect, circle])).renderAll();
|
||||
canvas.setActiveObject(new fabric.ActiveSelection([rect, circle]));
|
||||
var svgWithActiveGroup = canvas.toSVG();
|
||||
|
||||
equal(svg, svgWithActiveGroup);
|
||||
|
|
@ -1363,13 +1356,13 @@
|
|||
equal(canvas._objects[1], rect2);
|
||||
equal(canvas._objects[2], circle1);
|
||||
equal(canvas._objects[3], circle2);
|
||||
var aGroup = new fabric.Group([rect2, circle2, rect1, circle1]);
|
||||
var aGroup = new fabric.ActiveSelection([rect2, circle2, rect1, circle1]);
|
||||
// before rendering objects are ordered in insert order
|
||||
equal(aGroup._objects[0], rect2);
|
||||
equal(aGroup._objects[1], circle2);
|
||||
equal(aGroup._objects[2], rect1);
|
||||
equal(aGroup._objects[3], circle1);
|
||||
canvas.setActiveGroup(aGroup).renderAll();
|
||||
canvas.setActiveObject(aGroup).renderAll();
|
||||
// after rendering objects are ordered in canvas stack order
|
||||
equal(aGroup._objects[0], rect1);
|
||||
equal(aGroup._objects[1], rect2);
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@
|
|||
QUnit.module('fabric.Group', {
|
||||
teardown: function() {
|
||||
canvas.clear();
|
||||
canvas.setActiveGroup(null);
|
||||
canvas.backgroundColor = fabric.Canvas.prototype.backgroundColor;
|
||||
canvas.calcOffset();
|
||||
}
|
||||
|
|
@ -484,26 +483,6 @@
|
|||
equal(group.insertAt(rect1, 2), group, 'should be chainable');
|
||||
});
|
||||
|
||||
test('canvas property propagation', function() {
|
||||
var g1 = makeGroupWith4Objects(),
|
||||
g2 = makeGroupWith4Objects(),
|
||||
rect1 = new fabric.Rect(),
|
||||
rect2 = new fabric.Rect(),
|
||||
group1 = new fabric.Group([g1]);
|
||||
|
||||
group1.add(g2);
|
||||
group1.insertAt(rect1, 0);
|
||||
g2.insertAt(rect2, 0);
|
||||
|
||||
canvas.add(group1);
|
||||
equal(g2.canvas, canvas);
|
||||
equal(g2._objects[3].canvas, canvas);
|
||||
equal(g1.canvas, canvas);
|
||||
equal(g1._objects[3].canvas, canvas);
|
||||
equal(rect2.canvas, canvas);
|
||||
equal(rect1.canvas, canvas);
|
||||
});
|
||||
|
||||
test('dirty flag propagation from children up', function() {
|
||||
var g1 = makeGroupWith4Objects();
|
||||
var obj = g1.item(0);
|
||||
|
|
|
|||
Loading…
Reference in a new issue