konva/src/Container.js

416 lines
13 KiB
JavaScript
Raw Normal View History

(function() {
Kinetic.Util.addMethods(Kinetic.Container, {
__init: function(config) {
this.children = new Kinetic.Collection();
Kinetic.Node.call(this, config);
},
/**
* returns a {@link Kinetic.Collection} of direct descendant nodes
* @method
* @memberof Kinetic.Container.prototype
*/
getChildren: function() {
return this.children;
},
/**
* determine if node has children
* @method
* @memberof Kinetic.Container.prototype
2014-01-13 01:07:57 +08:00
* @returns {Boolean}
*/
hasChildren: function() {
return this.getChildren().length > 0;
},
/**
* remove all children
* @method
* @memberof Kinetic.Container.prototype
*/
removeChildren: function() {
var children = Kinetic.Collection.toCollection(this.children);
var child;
for (var i = 0; i < children.length; i++) {
child = children[i];
// reset parent to prevent many _setChildrenIndices calls
delete child.parent;
child.index = 0;
if (child.hasChildren()) {
child.removeChildren();
}
child.remove();
}
children = null;
this.children = new Kinetic.Collection();
return this;
},
2013-06-07 14:03:00 +08:00
/**
* destroy all children
* @method
* @memberof Kinetic.Container.prototype
*/
destroyChildren: function() {
var children = Kinetic.Collection.toCollection(this.children);
var child;
for (var i = 0; i < children.length; i++) {
child = children[i];
// reset parent to prevent many _setChildrenIndices calls
delete child.parent;
child.index = 0;
child.destroy();
2013-06-07 14:03:00 +08:00
}
children = null;
this.children = new Kinetic.Collection();
2013-06-07 14:03:00 +08:00
return this;
},
/**
* add node to container
* @method
* @memberof Kinetic.Container.prototype
* @param {Node} child
2014-01-13 01:05:46 +08:00
* @returns {Container}
*/
add: function(child) {
if (child.getParent()) {
child.moveTo(this);
return;
}
var children = this.children;
this._validateAdd(child);
child.index = children.length;
child.parent = this;
children.push(child);
2013-05-21 13:12:43 +08:00
this._fire('add', {
child: child
2013-05-21 13:12:43 +08:00
});
// chainable
return this;
},
destroy: function() {
// destroy children
if (this.hasChildren()) {
this.destroyChildren();
}
// then destroy self
Kinetic.Node.prototype.destroy.call(this);
},
/**
* return a {@link Kinetic.Collection} of nodes that match the selector. Use '#' for id selections
* and '.' for name selections. You can also select by type or class name. Pass multiple selectors
* separated by a space.
* @method
* @memberof Kinetic.Container.prototype
* @param {String} selector
2014-01-13 01:07:06 +08:00
* @returns {Collection}
* @example
* // select node with id foo<br>
* var node = stage.find('#foo');<br><br>
*
* // select nodes with name bar inside layer<br>
* var nodes = layer.find('.bar');<br><br>
*
* // select all groups inside layer<br>
* var nodes = layer.find('Group');<br><br>
*
* // select all rectangles inside layer<br>
* var nodes = layer.find('Rect');<br><br>
*
* // select node with an id of foo or a name of bar inside layer<br>
* var nodes = layer.find('#foo, .bar');
*/
find: function(selector) {
var retArr = [],
selectorArr = selector.replace(/ /g, '').split(','),
len = selectorArr.length,
n, i, sel, arr, node, children, clen;
for (n = 0; n < len; n++) {
sel = selectorArr[n];
// id selector
if(sel.charAt(0) === '#') {
node = this._getNodeById(sel.slice(1));
if(node) {
retArr.push(node);
}
}
// name selector
else if(sel.charAt(0) === '.') {
arr = this._getNodesByName(sel.slice(1));
retArr = retArr.concat(arr);
}
// unrecognized selector, pass to children
else {
children = this.getChildren();
clen = children.length;
for(i = 0; i < clen; i++) {
retArr = retArr.concat(children[i]._get(sel));
}
}
}
return Kinetic.Collection.toCollection(retArr);
},
_getNodeById: function(key) {
var node = Kinetic.ids[key];
if(node !== undefined && this.isAncestorOf(node)) {
return node;
}
return null;
},
_getNodesByName: function(key) {
var arr = Kinetic.names[key] || [];
return this._getDescendants(arr);
},
_get: function(selector) {
var retArr = Kinetic.Node.prototype._get.call(this, selector);
var children = this.getChildren();
var len = children.length;
for(var n = 0; n < len; n++) {
retArr = retArr.concat(children[n]._get(selector));
}
return retArr;
},
// extenders
toObject: function() {
var obj = Kinetic.Node.prototype.toObject.call(this);
obj.children = [];
var children = this.getChildren();
var len = children.length;
for(var n = 0; n < len; n++) {
var child = children[n];
obj.children.push(child.toObject());
}
return obj;
},
_getDescendants: function(arr) {
var retArr = [];
var len = arr.length;
for(var n = 0; n < len; n++) {
var node = arr[n];
if(this.isAncestorOf(node)) {
retArr.push(node);
}
}
return retArr;
},
/**
* determine if node is an ancestor
* of descendant
* @method
* @memberof Kinetic.Container.prototype
* @param {Kinetic.Node} node
*/
isAncestorOf: function(node) {
var parent = node.getParent();
while(parent) {
if(parent._id === this._id) {
return true;
}
parent = parent.getParent();
}
2012-09-26 03:06:02 +08:00
return false;
},
clone: function(obj) {
// call super method
var node = Kinetic.Node.prototype.clone.call(this, obj);
this.getChildren().each(function(no) {
node.add(no.clone());
});
return node;
},
/**
* get all shapes that intersect a point. Note: because this method must clear a temporary
* canvas and redraw every shape inside the container, it should only be used for special sitations
* because it performs very poorly. Please use the {@link Kinetic.Stage#getIntersection} method if at all possible
* because it performs much better
* @method
* @memberof Kinetic.Container.prototype
* @param {Object} pos
* @param {Number} pos.x
* @param {Number} pos.y
* @returns {Array} array of shapes
*/
getAllIntersections: function(pos) {
var arr = [];
this.find('Shape').each(function(shape) {
if(shape.isVisible() && shape.intersects(pos)) {
arr.push(shape);
}
});
return arr;
},
_setChildrenIndices: function() {
this.children.each(function(child, n) {
child.index = n;
});
},
drawScene: function(can) {
var layer = this.getLayer(),
canvas = can || (layer && layer.getCanvas()),
context = canvas && canvas.getContext(),
cachedCanvas = this._cache.canvas,
cachedSceneCanvas = cachedCanvas && cachedCanvas.scene;
if (this.isVisible()) {
if (cachedSceneCanvas) {
this._drawCachedSceneCanvas(context);
}
else {
this._drawChildren(canvas, 'drawScene');
}
}
return this;
},
drawHit: function(can) {
var layer = this.getLayer(),
canvas = can || (layer && layer.hitCanvas),
context = canvas && canvas.getContext(),
cachedCanvas = this._cache.canvas,
cachedHitCanvas = cachedCanvas && cachedCanvas.hit;
if (this.shouldDrawHit()) {
if (cachedHitCanvas) {
this._drawCachedHitCanvas(context);
}
else {
this._drawChildren(canvas, 'drawHit');
}
}
return this;
},
_drawChildren: function(canvas, drawMethod) {
var context = canvas && canvas.getContext(),
clipWidth = this.getClipWidth(),
clipHeight = this.getClipHeight(),
hasClip = clipWidth && clipHeight,
clipX, clipY;
if (hasClip) {
clipX = this.getClipX();
clipY = this.getClipY();
context.save();
context._applyTransform(this);
context.beginPath();
context.rect(clipX, clipY, clipWidth, clipHeight);
context.clip();
2014-02-27 08:49:18 +08:00
context.reset();
}
this.children.each(function(child) {
child[drawMethod](canvas);
});
if (hasClip) {
context.restore();
}
}
});
Kinetic.Util.extend(Kinetic.Container, Kinetic.Node);
// deprecated methods
Kinetic.Container.prototype.get = Kinetic.Container.prototype.find;
// add getters setters
Kinetic.Factory.addComponentsGetterSetter(Kinetic.Container, 'clip', ['x', 'y', 'width', 'height']);
/**
2014-01-06 12:52:09 +08:00
* get/set clip
* @method
2014-01-06 12:52:09 +08:00
* @name clip
* @memberof Kinetic.Container.prototype
2014-01-06 12:52:09 +08:00
* @param {Object} clip
* @param {Number} clip.x
* @param {Number} clip.y
* @param {Number} clip.width
* @param {Number} clip.height
* @returns {Object}
* @example
2014-01-06 12:52:09 +08:00
* // get clip<br>
* var clip = container.clip();<br><br>
*
* // set clip<br>
* container.setClip({<br>
* x: 20,<br>
* y: 20,<br>
* width: 20,<br>
* height: 20<br>
* });
*/
Kinetic.Factory.addGetterSetter(Kinetic.Container, 'clipX');
/**
2014-01-06 12:52:09 +08:00
* get/set clip x
* @name clipX
* @method
* @memberof Kinetic.Container.prototype
* @param {Number} x
* @returns {Number}
2014-01-06 12:52:09 +08:00
* @example
* // get clip x<br>
* var clipX = container.clipX();<br><br>
*
* // set clip x<br>
* container.clipX(10);
*/
Kinetic.Factory.addGetterSetter(Kinetic.Container, 'clipY');
/**
2014-01-06 12:52:09 +08:00
* get/set clip y
* @name clipY
* @method
* @memberof Kinetic.Container.prototype
2014-01-06 12:52:09 +08:00
* @param {Number} y
* @returns {Number}
2014-01-06 12:52:09 +08:00
* @example
* // get clip y<br>
* var clipY = container.clipY();<br><br>
*
* // set clip y<br>
* container.clipY(10);
*/
Kinetic.Factory.addGetterSetter(Kinetic.Container, 'clipWidth');
/**
2014-01-06 12:52:09 +08:00
* get/set clip width
* @name clipWidth
* @method
* @memberof Kinetic.Container.prototype
2014-01-06 12:52:09 +08:00
* @param {Number} width
* @returns {Number}
2014-01-06 12:52:09 +08:00
* @example
* // get clip width<br>
* var clipWidth = container.clipWidth();<br><br>
*
* // set clip width<br>
* container.clipWidth(100);
*/
Kinetic.Factory.addGetterSetter(Kinetic.Container, 'clipHeight');
/**
2014-01-06 12:52:09 +08:00
* get/set clip height
* @name clipHeight
* @method
* @memberof Kinetic.Container.prototype
2014-01-06 12:52:09 +08:00
* @param {Number} height
* @returns {Number}
2014-01-06 12:52:09 +08:00
* @example
* // get clip height<br>
* var clipHeight = container.clipHeight();<br><br>
*
* // set clip height<br>
* container.clipHeight(100);
*/
2014-02-27 08:49:18 +08:00
Kinetic.Collection.mapMethods(Kinetic.Container);
})();