/*
* Container
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
// namespace:
this.createjs = this.createjs||{};
(function() {
/**
* A Container is a nestable display list that allows you to work with compound display elements. For example you could
* group arm, leg, torso and head {{#crossLink "Bitmap"}}{{/crossLink}} instances together into a Person Container, and
* transform them as a group, while still being able to move the individual parts relative to each other. Children of
* containers have their transform
and alpha
properties concatenated with their parent
* Container.
*
* For example, a {{#crossLink "Shape"}}{{/crossLink}} with x=100 and alpha=0.5, placed in a Container with x=50
* and alpha=0.7
will be rendered to the canvas at x=150
and alpha=0.35
.
* Containers have some overhead, so you generally shouldn't create a Container to hold a single child.
*
*
Example
* var container = new createjs.Container();
* container.addChild(bitmapInstance, shapeInstance);
* container.x = 100;
*
* @class Container
* @extends DisplayObject
* @constructor
**/
var Container = function() {
this.initialize();
}
var p = Container.prototype = new createjs.DisplayObject();
// public properties:
/**
* The array of children in the display list. You should usually use the child management methods such as {{#crossLink "Container/addChild"}}{{/crossLink}},
* {{#crossLink "Container/removeChild"}}{{/crossLink}}, {{#crossLink "Container/swapChildren"}}{{/crossLink}}, etc,
* rather than accessing this directly, but it is included for advanced users.
* @property children
* @type Array
* @default null
**/
p.children = null;
// constructor:
/**
* @property DisplayObject_initialize
* @type Function
* @private
**/
p.DisplayObject_initialize = p.initialize;
/**
* Initialization method.
* @method initialize
* @protected
*/
p.initialize = function() {
this.DisplayObject_initialize();
this.children = [];
}
// public methods:
/**
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
* This does not account for whether it would be visible within the boundaries of the stage.
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
* @method isVisible
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
**/
p.isVisible = function() {
var hasContent = this.cacheCanvas || this.children.length;
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
}
/**
* @property DisplayObject_draw
* @type Function
* @private
**/
p.DisplayObject_draw = p.draw;
/**
* Draws the display object into the specified context ignoring it's visible, alpha, shadow, and transform.
* Returns true if the draw was handled (useful for overriding functionality).
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
* @method draw
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
* @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
* For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
* into itself).
**/
p.draw = function(ctx, ignoreCache) {
if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
// this ensures we don't have issues with display list changes that occur during a draw:
var list = this.children.slice(0);
for (var i=0,l=list.length; iExample
* container.addChild(bitmapInstance, shapeInstance);
*
* @method addChild
* @param {DisplayObject} child The display object to add.
* @return {DisplayObject} The child that was added, or the last child if multiple children were added.
**/
p.addChild = function(child) {
if (child == null) { return child; }
var l = arguments.length;
if (l > 1) {
for (var i=0; i this.children.length) { return arguments[l-2]; }
if (l > 2) {
for (var i=0; i 1) {
var good = true;
for (var i=0; i 1) {
var a = [];
for (var i=0; i this.children.length-1) { return false; }
var child = this.children[index];
if (child) { child.parent = null; }
this.children.splice(index, 1);
return true;
}
/**
* Removes all children from the display list.
* @method removeAllChildren
**/
p.removeAllChildren = function() {
var kids = this.children;
while (kids.length) { kids.pop().parent = null; }
}
/**
* Returns the child at the specified index.
* @method getChildAt
* @param {Number} index The index of the child to return.
* @return {DisplayObject} The child at the specified index.
**/
p.getChildAt = function(index) {
return this.children[index];
}
/**
* Returns the child with the specified name.
* @method getChildByName
* @param {String} name The name of the child to return.
* @return {DisplayObject} The child with the specified name.
**/
p.getChildByName = function(name) {
var kids = this.children;
for (var i=0,l=kids.length;i= l) { return; }
for (var i=0;i 0 at the specified
* position). This ignores the alpha, shadow and compositeOperation of the display object, and all transform properties
* including regX/Y.
* @method hitTest
* @param {Number} x The x position to check in the display object's local coordinates.
* @param {Number} y The y position to check in the display object's local coordinates.
* @return {Boolean} A Boolean indicating whether there is a visible section of a DisplayObject that overlaps the specified
* coordinates.
**/
p.hitTest = function(x, y) {
// TODO: optimize to use the fast cache check where possible.
return (this.getObjectUnderPoint(x, y) != null);
}
/**
* Returns an array of all display objects under the specified coordinates that are in this container's display list.
* This routine ignores any display objects with mouseEnabled set to false. The array will be sorted in order of visual
* depth, with the top-most display object at index 0. This uses shape based hit detection, and can be an expensive operation
* to run, so it is best to use it carefully. For example, if testing for objects under the mouse, test on tick (instead of on
* mousemove), and only if the mouse's position has changed.
* @method getObjectsUnderPoint
* @param {Number} x The x position in the container to test.
* @param {Number} y The y position in the container to test.
* @return {Array} An Array of DisplayObjects under the specified coordinates.
**/
p.getObjectsUnderPoint = function(x, y) {
var arr = [];
var pt = this.localToGlobal(x, y);
this._getObjectsUnderPoint(pt.x, pt.y, arr);
return arr;
}
/**
* Similar to getObjectsUnderPoint(), but returns only the top-most display object. This runs significantly faster than
* getObjectsUnderPoint(), but is still an expensive operation. See getObjectsUnderPoint() for more information.
* @method getObjectUnderPoint
* @param {Number} x The x position in the container to test.
* @param {Number} y The y position in the container to test.
* @return {DisplayObject} The top-most display object under the specified coordinates.
**/
p.getObjectUnderPoint = function(x, y) {
var pt = this.localToGlobal(x, y);
return this._getObjectsUnderPoint(pt.x, pt.y);
}
/**
* Returns a clone of this Container. Some properties that are specific to this instance's current context are reverted to
* their defaults (for example .parent).
* @param {Boolean} recursive If true, all of the descendants of this container will be cloned recursively. If false, the
* properties of the container will be cloned, but the new instance will not have any children.
* @return {Container} A clone of the current Container instance.
**/
p.clone = function(recursive) {
var o = new Container();
this.cloneProps(o);
if (recursive) {
var arr = o.children = [];
for (var i=0, l=this.children.length; i=0; i--) {
var child = this.children[i];
if (child._tick) { child._tick(params); }
}
this.DisplayObject__tick(params);
}
/**
* @method _getObjectsUnderPoint
* @param {Number} x
* @param {Number} y
* @param {Array} arr
* @param {Number} mouseEvents A bitmask indicating which event types to look for. Bit 1 specifies press &
* click & double click, bit 2 specifies it should look for mouse over and mouse out. This implementation may change.
* @return {Array}
* @protected
**/
p._getObjectsUnderPoint = function(x, y, arr, mouseEvents) {
var ctx = createjs.DisplayObject._hitTestContext;
var mtx = this._matrix;
var hasHandler = this._hasMouseHandler(mouseEvents);
// if we have a cache handy & this has a handler, we can use it to do a quick check.
// we can't use the cache for screening children, because they might have hitArea set.
if (!this.hitArea && this.cacheCanvas && hasHandler) {
this.getConcatenatedMatrix(mtx);
ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y);
ctx.globalAlpha = mtx.alpha;
this.draw(ctx);
if (this._testHit(ctx)) {
ctx.setTransform(1, 0, 0, 1, 0, 0)
ctx.clearRect(0,0,1,1);
return this;
}
}
// draw children one at a time, and check if we get a hit:
var l = this.children.length;
for (var i=l-1; i>=0; i--) {
var child = this.children[i];
var hitArea = child.hitArea;
if (!child.visible || (!hitArea && !child.isVisible()) || (mouseEvents && !child.mouseEnabled)) { continue; }
var childHasHandler = mouseEvents && child._hasMouseHandler(mouseEvents);
// if a child container has a handler and a hitArea then we only need to check its hitArea, so we can treat it as a normal DO:
if (child instanceof Container && !(hitArea && childHasHandler)) {
var result;
if (hasHandler) {
// only concerned about the first hit, because this container is going to claim it anyway:
result = child._getObjectsUnderPoint(x, y);
if (result) { return this; }
} else {
result = child._getObjectsUnderPoint(x, y, arr, mouseEvents);
if (!arr && result) { return result; }
}
} else if (!mouseEvents || hasHandler || childHasHandler) {
child.getConcatenatedMatrix(mtx);
if (hitArea) {
mtx.appendTransform(hitArea.x, hitArea.y, hitArea.scaleX, hitArea.scaleY, hitArea.rotation, hitArea.skewX, hitArea.skewY, hitArea.regX, hitArea.regY);
mtx.alpha = hitArea.alpha;
}
ctx.globalAlpha = mtx.alpha;
ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y);
(hitArea||child).draw(ctx);
if (!this._testHit(ctx)) { continue; }
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, 1, 1);
if (hasHandler) { return this; }
else if (arr) { arr.push(child); }
else { return child; }
}
}
return null;
};
createjs.Container = Container;
}());