/* * 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; }());