mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-05-22 12:21:51 +00:00
Initial implementation of generic groups. See https://github.com/kangax/fabric.js/wiki/Working-with-groups for more info.
This commit is contained in:
parent
feaddda1c0
commit
83e3c8bd9e
9 changed files with 193 additions and 129 deletions
159
dist/all.js
vendored
159
dist/all.js
vendored
|
|
@ -1947,6 +1947,46 @@ fabric.Observable = {
|
|||
callback && callback.call(context, url);
|
||||
}
|
||||
}
|
||||
|
||||
function enlivenObjects(objects, callback) {
|
||||
|
||||
function getKlass(type) {
|
||||
return fabric[fabric.util.string.camelize(fabric.util.string.capitalize(type))];
|
||||
}
|
||||
|
||||
var enlivenedObjects = [ ],
|
||||
numLoadedAsyncObjects = 0,
|
||||
// get length of all images
|
||||
numTotalAsyncObjects = objects.filter(function (o) {
|
||||
return getKlass(o.type).async;
|
||||
}).length;
|
||||
|
||||
var _this = this;
|
||||
|
||||
objects.forEach(function (o, index) {
|
||||
if (!o.type) {
|
||||
return;
|
||||
}
|
||||
var klass = getKlass(o.type);
|
||||
if (klass.async) {
|
||||
klass.fromObject(o, function (o) {
|
||||
enlivenedObjects[index] = o;
|
||||
if (++numLoadedAsyncObjects === numTotalAsyncObjects) {
|
||||
if (callback) {
|
||||
callback(enlivenedObjects);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
enlivenedObjects[index] = klass.fromObject(o);
|
||||
}
|
||||
});
|
||||
|
||||
if (numTotalAsyncObjects === 0 && callback) {
|
||||
callback(enlivenedObjects);
|
||||
}
|
||||
}
|
||||
|
||||
fabric.util.removeFromArray = removeFromArray;
|
||||
fabric.util.degreesToRadians = degreesToRadians;
|
||||
|
|
@ -1956,6 +1996,7 @@ fabric.Observable = {
|
|||
fabric.util.animate = animate;
|
||||
fabric.util.requestAnimFrame = requestAnimFrame;
|
||||
fabric.util.loadImage = loadImage;
|
||||
fabric.util.enlivenObjects = enlivenObjects;
|
||||
})();
|
||||
(function() {
|
||||
|
||||
|
|
@ -5677,7 +5718,7 @@ fabric.util.string = {
|
|||
}
|
||||
|
||||
this._currentTransform = null;
|
||||
|
||||
|
||||
if (this._groupSelector) {
|
||||
// group selection was completed, determine its bounds
|
||||
this._findSelectedObjects(e);
|
||||
|
|
@ -5737,7 +5778,7 @@ fabric.util.string = {
|
|||
pointer = this.getPointer(e),
|
||||
activeGroup = this.getActiveGroup(),
|
||||
corner;
|
||||
|
||||
|
||||
if (this._shouldClearSelection(e)) {
|
||||
|
||||
this._groupSelector = {
|
||||
|
|
@ -5994,7 +6035,7 @@ fabric.util.string = {
|
|||
},
|
||||
|
||||
_handleGroupLogic: function (e, target) {
|
||||
if (target.isType('group')) {
|
||||
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
|
||||
|
|
@ -6005,7 +6046,7 @@ fabric.util.string = {
|
|||
var activeGroup = this.getActiveGroup();
|
||||
if (activeGroup) {
|
||||
if (activeGroup.contains(target)) {
|
||||
activeGroup.remove(target);
|
||||
activeGroup.removeWithUpdate(target);
|
||||
target.setActive(false);
|
||||
if (activeGroup.size() === 1) {
|
||||
// remove group alltogether if after removal it only contains 1 object
|
||||
|
|
@ -6013,7 +6054,7 @@ fabric.util.string = {
|
|||
}
|
||||
}
|
||||
else {
|
||||
activeGroup.add(target);
|
||||
activeGroup.addWithUpdate(target);
|
||||
}
|
||||
this.fire('selection:created', { target: activeGroup, e: e });
|
||||
activeGroup.setActive(true);
|
||||
|
|
@ -6024,7 +6065,7 @@ fabric.util.string = {
|
|||
// only if there's an active object
|
||||
if (target !== this._activeObject) {
|
||||
// and that object is not the actual target
|
||||
var group = new fabric.Group([ this._activeObject,target ]);
|
||||
var group = new fabric.Group([ this._activeObject, target ]);
|
||||
this.setActiveGroup(group);
|
||||
activeGroup = this.getActiveGroup();
|
||||
}
|
||||
|
|
@ -6463,6 +6504,7 @@ fabric.util.string = {
|
|||
*/
|
||||
setActiveGroup: function (group) {
|
||||
this._activeGroup = group;
|
||||
group && group.setActive(true);
|
||||
return this;
|
||||
},
|
||||
|
||||
|
|
@ -6833,42 +6875,12 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
|
|||
* @param {Function} callback
|
||||
*/
|
||||
_enlivenObjects: function (objects, callback) {
|
||||
var numLoadedImages = 0,
|
||||
// get length of all images
|
||||
numTotalImages = objects.filter(function (o) {
|
||||
return o.type === 'image';
|
||||
}).length;
|
||||
|
||||
var _this = this;
|
||||
|
||||
objects.forEach(function (o, index) {
|
||||
if (!o.type) {
|
||||
return;
|
||||
}
|
||||
switch (o.type) {
|
||||
case 'image':
|
||||
case 'font':
|
||||
fabric[fabric.util.string.capitalize(o.type)].fromObject(o, function (o) {
|
||||
_this.insertAt(o, index, true);
|
||||
if (++numLoadedImages === numTotalImages) {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
var klass = fabric[fabric.util.string.camelize(fabric.util.string.capitalize(o.type))];
|
||||
if (klass && klass.fromObject) {
|
||||
_this.insertAt(klass.fromObject(o), index, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
fabric.util.enlivenObjects(objects, function(enlivenedObjects) {
|
||||
enlivenedObjects.forEach(function(obj, index) {
|
||||
_this.insertAt(obj, index, true);
|
||||
});
|
||||
});
|
||||
|
||||
if (numTotalImages === 0 && callback) {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -10409,7 +10421,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
|
|||
this.setCoords(true);
|
||||
this.saveCoords();
|
||||
|
||||
this.activateAllObjects();
|
||||
//this.activateAllObjects();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -10458,15 +10470,14 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
|
|||
|
||||
/**
|
||||
* Adds an object to a group; Then recalculates group's dimension, position.
|
||||
* @method add
|
||||
* @method addWithUpdate
|
||||
* @param {Object} object
|
||||
* @return {fabric.Group} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
add: function(object) {
|
||||
addWithUpdate: function(object) {
|
||||
this._restoreObjectsState();
|
||||
this.objects.push(object);
|
||||
object.setActive(true);
|
||||
this._calcBounds();
|
||||
this._updateObjectsCoords();
|
||||
return this;
|
||||
|
|
@ -10474,11 +10485,12 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
|
|||
|
||||
/**
|
||||
* Removes an object from a group; Then recalculates group's dimension, position.
|
||||
* @method removeWithUpdate
|
||||
* @param {Object} object
|
||||
* @return {fabric.Group} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
remove: function(object) {
|
||||
removeWithUpdate: function(object) {
|
||||
this._restoreObjectsState();
|
||||
removeFromArray(this.objects, object);
|
||||
object.setActive(false);
|
||||
|
|
@ -10487,6 +10499,29 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
|
|||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds an object to a group
|
||||
* @method add
|
||||
* @param {Object} object
|
||||
* @return {fabric.Group} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
add: function(object) {
|
||||
this.objects.push(object);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes an object from a group
|
||||
* @method remove
|
||||
* @param {Object} object
|
||||
* @return {fabric.Group} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
remove: function(object) {
|
||||
removeFromArray(this.objects, object);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a size of a group (i.e: length of an array containing its objects)
|
||||
* @return {Number} Group size
|
||||
|
|
@ -10549,7 +10584,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
|
|||
* @method render
|
||||
* @param {CanvasRenderingContext2D} ctx context to render instance on
|
||||
*/
|
||||
render: function(ctx) {
|
||||
render: function(ctx, noTransform) {
|
||||
ctx.save();
|
||||
this.transform(ctx);
|
||||
|
||||
|
|
@ -10561,8 +10596,10 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
|
|||
object.render(ctx);
|
||||
object.borderScaleFactor = originalScaleFactor;
|
||||
}
|
||||
this.hideBorders || this.drawBorders(ctx);
|
||||
this.hideCorners || this.drawCorners(ctx);
|
||||
if (!noTransform && this.active) {
|
||||
this.drawBorders(ctx);
|
||||
this.hideCorners || this.drawCorners(ctx);
|
||||
}
|
||||
ctx.restore();
|
||||
this.setCoords();
|
||||
},
|
||||
|
|
@ -10686,19 +10723,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
|
|||
* @chainable
|
||||
*/
|
||||
activateAllObjects: function() {
|
||||
return this.setActive(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Activates (makes active) all group objects
|
||||
* @method setActive
|
||||
* @param {Boolean} value `true` to activate object, `false` otherwise
|
||||
* @return {fabric.Group} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
setActive: function(value) {
|
||||
this.forEachObject(function(object) {
|
||||
object.setActive(value);
|
||||
object.setActive();
|
||||
});
|
||||
return this;
|
||||
},
|
||||
|
|
@ -10811,10 +10837,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
|
|||
* @param options {Object} options object
|
||||
* @return {fabric.Group} an instance of fabric.Group
|
||||
*/
|
||||
fabric.Group.fromObject = function(object) {
|
||||
return new fabric.Group(object.objects, object);
|
||||
fabric.Group.fromObject = function(object, callback) {
|
||||
fabric.util.enlivenObjects(object.objects, function(enlivenedObjects) {
|
||||
delete object.objects;
|
||||
callback(new fabric.Group(enlivenedObjects, object));
|
||||
});
|
||||
};
|
||||
|
||||
fabric.Group.async = true;
|
||||
|
||||
})(typeof exports != 'undefined' ? exports : this);
|
||||
//= require "object.class"
|
||||
|
||||
|
|
@ -11268,7 +11299,6 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
|
|||
fabric.Image.async = true;
|
||||
|
||||
})(typeof exports != 'undefined' ? exports : this);
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
*/
|
||||
|
|
@ -11934,9 +11964,10 @@ fabric.Image.filters.Invert.fromObject = function() {
|
|||
fabric.Text.fromElement = function(element) {
|
||||
// TODO (kangax): implement this
|
||||
};
|
||||
|
||||
fabric.Text.async = true;
|
||||
|
||||
})(typeof exports != 'undefined' ? exports : this);
|
||||
|
||||
(function() {
|
||||
|
||||
if (typeof document != 'undefined' && typeof window != 'undefined') {
|
||||
|
|
|
|||
2
dist/all.min.js
vendored
2
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.
|
|
@ -225,7 +225,7 @@
|
|||
}
|
||||
|
||||
this._currentTransform = null;
|
||||
|
||||
|
||||
if (this._groupSelector) {
|
||||
// group selection was completed, determine its bounds
|
||||
this._findSelectedObjects(e);
|
||||
|
|
@ -285,7 +285,7 @@
|
|||
pointer = this.getPointer(e),
|
||||
activeGroup = this.getActiveGroup(),
|
||||
corner;
|
||||
|
||||
|
||||
if (this._shouldClearSelection(e)) {
|
||||
|
||||
this._groupSelector = {
|
||||
|
|
@ -542,7 +542,7 @@
|
|||
},
|
||||
|
||||
_handleGroupLogic: function (e, target) {
|
||||
if (target.isType('group')) {
|
||||
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
|
||||
|
|
@ -553,7 +553,7 @@
|
|||
var activeGroup = this.getActiveGroup();
|
||||
if (activeGroup) {
|
||||
if (activeGroup.contains(target)) {
|
||||
activeGroup.remove(target);
|
||||
activeGroup.removeWithUpdate(target);
|
||||
target.setActive(false);
|
||||
if (activeGroup.size() === 1) {
|
||||
// remove group alltogether if after removal it only contains 1 object
|
||||
|
|
@ -561,7 +561,7 @@
|
|||
}
|
||||
}
|
||||
else {
|
||||
activeGroup.add(target);
|
||||
activeGroup.addWithUpdate(target);
|
||||
}
|
||||
this.fire('selection:created', { target: activeGroup, e: e });
|
||||
activeGroup.setActive(true);
|
||||
|
|
@ -572,7 +572,7 @@
|
|||
// only if there's an active object
|
||||
if (target !== this._activeObject) {
|
||||
// and that object is not the actual target
|
||||
var group = new fabric.Group([ this._activeObject,target ]);
|
||||
var group = new fabric.Group([ this._activeObject, target ]);
|
||||
this.setActiveGroup(group);
|
||||
activeGroup = this.getActiveGroup();
|
||||
}
|
||||
|
|
@ -1011,6 +1011,7 @@
|
|||
*/
|
||||
setActiveGroup: function (group) {
|
||||
this._activeGroup = group;
|
||||
group && group.setActive(true);
|
||||
return this;
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -179,42 +179,12 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
|
|||
* @param {Function} callback
|
||||
*/
|
||||
_enlivenObjects: function (objects, callback) {
|
||||
var numLoadedImages = 0,
|
||||
// get length of all images
|
||||
numTotalImages = objects.filter(function (o) {
|
||||
return o.type === 'image';
|
||||
}).length;
|
||||
|
||||
var _this = this;
|
||||
|
||||
objects.forEach(function (o, index) {
|
||||
if (!o.type) {
|
||||
return;
|
||||
}
|
||||
switch (o.type) {
|
||||
case 'image':
|
||||
case 'font':
|
||||
fabric[fabric.util.string.capitalize(o.type)].fromObject(o, function (o) {
|
||||
_this.insertAt(o, index, true);
|
||||
if (++numLoadedImages === numTotalImages) {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
var klass = fabric[fabric.util.string.camelize(fabric.util.string.capitalize(o.type))];
|
||||
if (klass && klass.fromObject) {
|
||||
_this.insertAt(klass.fromObject(o), index, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
fabric.util.enlivenObjects(objects, function(enlivenedObjects) {
|
||||
enlivenedObjects.forEach(function(obj, index) {
|
||||
_this.insertAt(obj, index, true);
|
||||
});
|
||||
});
|
||||
|
||||
if (numTotalImages === 0 && callback) {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@
|
|||
this.setCoords(true);
|
||||
this.saveCoords();
|
||||
|
||||
this.activateAllObjects();
|
||||
//this.activateAllObjects();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -101,15 +101,14 @@
|
|||
|
||||
/**
|
||||
* Adds an object to a group; Then recalculates group's dimension, position.
|
||||
* @method add
|
||||
* @method addWithUpdate
|
||||
* @param {Object} object
|
||||
* @return {fabric.Group} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
add: function(object) {
|
||||
addWithUpdate: function(object) {
|
||||
this._restoreObjectsState();
|
||||
this.objects.push(object);
|
||||
object.setActive(true);
|
||||
this._calcBounds();
|
||||
this._updateObjectsCoords();
|
||||
return this;
|
||||
|
|
@ -117,11 +116,12 @@
|
|||
|
||||
/**
|
||||
* Removes an object from a group; Then recalculates group's dimension, position.
|
||||
* @method removeWithUpdate
|
||||
* @param {Object} object
|
||||
* @return {fabric.Group} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
remove: function(object) {
|
||||
removeWithUpdate: function(object) {
|
||||
this._restoreObjectsState();
|
||||
removeFromArray(this.objects, object);
|
||||
object.setActive(false);
|
||||
|
|
@ -130,6 +130,29 @@
|
|||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds an object to a group
|
||||
* @method add
|
||||
* @param {Object} object
|
||||
* @return {fabric.Group} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
add: function(object) {
|
||||
this.objects.push(object);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes an object from a group
|
||||
* @method remove
|
||||
* @param {Object} object
|
||||
* @return {fabric.Group} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
remove: function(object) {
|
||||
removeFromArray(this.objects, object);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a size of a group (i.e: length of an array containing its objects)
|
||||
* @return {Number} Group size
|
||||
|
|
@ -192,7 +215,7 @@
|
|||
* @method render
|
||||
* @param {CanvasRenderingContext2D} ctx context to render instance on
|
||||
*/
|
||||
render: function(ctx) {
|
||||
render: function(ctx, noTransform) {
|
||||
ctx.save();
|
||||
this.transform(ctx);
|
||||
|
||||
|
|
@ -204,8 +227,10 @@
|
|||
object.render(ctx);
|
||||
object.borderScaleFactor = originalScaleFactor;
|
||||
}
|
||||
this.hideBorders || this.drawBorders(ctx);
|
||||
this.hideCorners || this.drawCorners(ctx);
|
||||
if (!noTransform && this.active) {
|
||||
this.drawBorders(ctx);
|
||||
this.hideCorners || this.drawCorners(ctx);
|
||||
}
|
||||
ctx.restore();
|
||||
this.setCoords();
|
||||
},
|
||||
|
|
@ -329,19 +354,8 @@
|
|||
* @chainable
|
||||
*/
|
||||
activateAllObjects: function() {
|
||||
return this.setActive(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Activates (makes active) all group objects
|
||||
* @method setActive
|
||||
* @param {Boolean} value `true` to activate object, `false` otherwise
|
||||
* @return {fabric.Group} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
setActive: function(value) {
|
||||
this.forEachObject(function(object) {
|
||||
object.setActive(value);
|
||||
object.setActive();
|
||||
});
|
||||
return this;
|
||||
},
|
||||
|
|
@ -454,8 +468,13 @@
|
|||
* @param options {Object} options object
|
||||
* @return {fabric.Group} an instance of fabric.Group
|
||||
*/
|
||||
fabric.Group.fromObject = function(object) {
|
||||
return new fabric.Group(object.objects, object);
|
||||
fabric.Group.fromObject = function(object, callback) {
|
||||
fabric.util.enlivenObjects(object.objects, function(enlivenedObjects) {
|
||||
delete object.objects;
|
||||
callback(new fabric.Group(enlivenedObjects, object));
|
||||
});
|
||||
};
|
||||
|
||||
fabric.Group.async = true;
|
||||
|
||||
})(typeof exports != 'undefined' ? exports : this);
|
||||
|
|
@ -449,4 +449,4 @@
|
|||
|
||||
fabric.Image.async = true;
|
||||
|
||||
})(typeof exports != 'undefined' ? exports : this);
|
||||
})(typeof exports != 'undefined' ? exports : this);
|
||||
|
|
@ -491,5 +491,7 @@
|
|||
fabric.Text.fromElement = function(element) {
|
||||
// TODO (kangax): implement this
|
||||
};
|
||||
|
||||
fabric.Text.async = true;
|
||||
|
||||
})(typeof exports != 'undefined' ? exports : this);
|
||||
})(typeof exports != 'undefined' ? exports : this);
|
||||
|
|
@ -153,6 +153,46 @@
|
|||
callback && callback.call(context, url);
|
||||
}
|
||||
}
|
||||
|
||||
function enlivenObjects(objects, callback) {
|
||||
|
||||
function getKlass(type) {
|
||||
return fabric[fabric.util.string.camelize(fabric.util.string.capitalize(type))];
|
||||
}
|
||||
|
||||
var enlivenedObjects = [ ],
|
||||
numLoadedAsyncObjects = 0,
|
||||
// get length of all images
|
||||
numTotalAsyncObjects = objects.filter(function (o) {
|
||||
return getKlass(o.type).async;
|
||||
}).length;
|
||||
|
||||
var _this = this;
|
||||
|
||||
objects.forEach(function (o, index) {
|
||||
if (!o.type) {
|
||||
return;
|
||||
}
|
||||
var klass = getKlass(o.type);
|
||||
if (klass.async) {
|
||||
klass.fromObject(o, function (o) {
|
||||
enlivenedObjects[index] = o;
|
||||
if (++numLoadedAsyncObjects === numTotalAsyncObjects) {
|
||||
if (callback) {
|
||||
callback(enlivenedObjects);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
enlivenedObjects[index] = klass.fromObject(o);
|
||||
}
|
||||
});
|
||||
|
||||
if (numTotalAsyncObjects === 0 && callback) {
|
||||
callback(enlivenedObjects);
|
||||
}
|
||||
}
|
||||
|
||||
fabric.util.removeFromArray = removeFromArray;
|
||||
fabric.util.degreesToRadians = degreesToRadians;
|
||||
|
|
@ -162,4 +202,5 @@
|
|||
fabric.util.animate = animate;
|
||||
fabric.util.requestAnimFrame = requestAnimFrame;
|
||||
fabric.util.loadImage = loadImage;
|
||||
fabric.util.enlivenObjects = enlivenObjects;
|
||||
})();
|
||||
Loading…
Reference in a new issue