/* * DisplayObject * 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. */ /** * The EaselJS Javascript library provides a retained graphics mode for canvas including a full hierarchical display * list, a core interaction model, and helper classes to make working with 2D graphics in Canvas much easier. * EaselJS provides straight forward solutions for working with rich graphics and interactivity with HTML5 Canvas... * * <h4>Getting Started</h4> * To get started with Easel, create a {{#crossLink "Stage"}}{{/crossLink}} that wraps a CANVAS element, and add * {{#crossLink "DisplayObject"}}{{/crossLink}} instances as children. EaselJS supports: * <ul> * <li>Images using {{#crossLink "Bitmap"}}{{/crossLink}}</li> * <li>Vector graphics using {{#crossLink "Shape"}}{{/crossLink}} and {{#crossLink "Graphics"}}{{/crossLink}}</li> * <li>Animated bitmaps using {{#crossLink "SpriteSheet"}}{{/crossLink}} and {{#crossLink "Sprite"}}{{/crossLink}}</li> * <li>Simple text instances using {{#crossLink "Text"}}{{/crossLink}}</li> * <li>Containers that hold other DisplayObjects using {{#crossLink "Container"}}{{/crossLink}}</li> * <li>Control HTML DOM elements using {{#crossLink "DOMElement"}}{{/crossLink}}</li> * </ul> * * All display objects can be added to the stage as children, or drawn to a canvas directly. * * <b>User Interactions</b> * * All display objects on stage (except {{#crossLink "DOMElement"}}{{/crossLink}}) will dispatch events when interacted * with using a mouse or touch. EaselJS supports hover, press, and release events, as well as an easy-to-use * drag-and-drop model. Check out {{#crossLink "MouseEvent"}}{{/crossLink}} for more information. * * <h4>Simple Example</h4> * This example illustrates how to create and position a {{#crossLink "Shape"}}{{/crossLink}} on the {{#crossLink "Stage"}}{{/crossLink}} * using EaselJS' drawing API. * * //Create a stage by getting a reference to the canvas * stage = new createjs.Stage("demoCanvas"); * //Create a Shape DisplayObject. * circle = new createjs.Shape(); * circle.graphics.beginFill("red").drawCircle(0, 0, 40); * //Set position of Shape instance. * circle.x = circle.y = 50; * //Add Shape instance to stage display list. * stage.addChild(circle); * //Update stage will render next frame * stage.update(); * * <b>Simple Interaction Example</b> * * displayObject.addEventListener("click", handleClick); * function handleClick(event){ * // Click happenened * } * * displayObject.addEventListener("mousedown", handlePress); * function handlePress(event) { * // A mouse press happened. * // Listen for mouse move while the mouse is down: * event.addEventListener("mousemove", handleMove); * } * function handleMove(event) { * // Check out the DragAndDrop example in GitHub for more * } * * <b>Simple Animation Example</b> * * This example moves the shape created in the previous demo across the screen. * * //Update stage will render next frame * createjs.Ticker.addEventListener("tick", handleTick); * * function handleTick() { * //Circle will move 10 units to the right. * circle.x += 10; * //Will cause the circle to wrap back * if (circle.x > stage.canvas.width) { circle.x = 0; } * stage.update(); * } * * <h4>Other Features</h4> * EaselJS also has built in support for * <ul> * <li>Canvas features such as {{#crossLink "Shadow"}}{{/crossLink}} and CompositeOperation</li> * <li>{{#crossLink "Ticker"}}{{/crossLink}}, a global heartbeat that objects can subscribe to</li> * <li>Filters, including a provided {{#crossLink "ColorMatrixFilter"}}{{/crossLink}}, {{#crossLink "AlphaMaskFilter"}}{{/crossLink}}, * {{#crossLink "AlphaMapFilter"}}{{/crossLink}}, and {{#crossLink "BlurFilter"}}{{/crossLink}}. See {{#crossLink "Filter"}}{{/crossLink}} * for more information</li> * <li>A {{#crossLink "ButtonHelper"}}{{/crossLink}} utility, to easily create interactive buttons</li> * <li>{{#crossLink "SpriteSheetUtils"}}{{/crossLink}} and a {{#crossLink "SpriteSheetBuilder"}}{{/crossLink}} to * help build and manage {{#crossLink "SpriteSheet"}}{{/crossLink}} functionality at run-time.</li> * </ul> * * <h4>Browser Support</h4> * All modern browsers that support Canvas will support EaselJS (<a href="http://caniuse.com/canvas">http://caniuse.com/canvas</a>). * Browser performance may vary between platforms, for example, Android Canvas has poor hardware support, and is much * slower on average than most other browsers. * * @module EaselJS * @main EaselJS */ // namespace: this.createjs = this.createjs||{}; (function() { "use strict"; // constructor: /** * DisplayObject is an abstract class that should not be constructed directly. Instead construct subclasses such as * {{#crossLink "Container"}}{{/crossLink}}, {{#crossLink "Bitmap"}}{{/crossLink}}, and {{#crossLink "Shape"}}{{/crossLink}}. * DisplayObject is the base class for all display classes in the EaselJS library. It defines the core properties and * methods that are shared between all display objects, such as transformation properties (x, y, scaleX, scaleY, etc), * caching, and mouse handlers. * @class DisplayObject * @extends EventDispatcher * @constructor **/ function DisplayObject() { this.EventDispatcher_constructor(); // public properties: /** * The alpha (transparency) for this display object. 0 is fully transparent, 1 is fully opaque. * @property alpha * @type {Number} * @default 1 **/ this.alpha = 1; /** * If a cache is active, this returns the canvas that holds the image of this display object. See {{#crossLink "DisplayObject/cache:method"}}{{/crossLink}} * for more information. Use this to display the result of a cache. This will be a HTMLCanvasElement unless special cache rules have been deliberately enabled for this cache. * @property cacheCanvas * @type {HTMLCanvasElement | Object} * @default null * @readonly **/ this.cacheCanvas = null; /** * If a cache has been made, this returns the class that is managing the cacheCanvas and its properties. See {{#crossLink "BitmapCache"}}{{/crossLink}} * for more information. Use this to control, inspect, and change the cache. In special circumstances this may be a modified or subclassed BitmapCache. * @property bitmapCache * @type {BitmapCache} * @default null * @readonly **/ this.bitmapCache = null; /** * Unique ID for this display object. Makes display objects easier for some uses. * @property id * @type {Number} * @default -1 **/ this.id = createjs.UID.get(); /** * Indicates whether to include this object when running mouse interactions. Setting this to `false` for children * of a {{#crossLink "Container"}}{{/crossLink}} will cause events on the Container to not fire when that child is * clicked. Setting this property to `false` does not prevent the {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}} * method from returning the child. * * <strong>Note:</strong> In EaselJS 0.7.0, the mouseEnabled property will not work properly with nested Containers. Please * check out the latest NEXT version in <a href="https://github.com/CreateJS/EaselJS/tree/master/lib">GitHub</a> for an updated version with this issue resolved. The fix will be * provided in the next release of EaselJS. * @property mouseEnabled * @type {Boolean} * @default true **/ this.mouseEnabled = true; /** * If false, the tick will not run on this display object (or its children). This can provide some performance benefits. * In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates * on some display objects (ex. Sprite & MovieClip frame advancing, and DOMElement display properties). * @property tickEnabled * @type Boolean * @default true **/ this.tickEnabled = true; /** * An optional name for this display object. Included in {{#crossLink "DisplayObject/toString"}}{{/crossLink}} . Useful for * debugging. * @property name * @type {String} * @default null **/ this.name = null; /** * A reference to the {{#crossLink "Container"}}{{/crossLink}} or {{#crossLink "Stage"}}{{/crossLink}} object that * contains this display object, or null if it has not been added * to one. * @property parent * @final * @type {Container} * @default null * @readonly **/ this.parent = null; /** * The left offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate * around its center, you would set regX and {{#crossLink "DisplayObject/regY:property"}}{{/crossLink}} to 50. * Cached object's registration points should be set based on pre-cache conditions, not cached size. * @property regX * @type {Number} * @default 0 **/ this.regX = 0; /** * The y offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate around * its center, you would set {{#crossLink "DisplayObject/regX:property"}}{{/crossLink}} and regY to 50. * Cached object's registration points should be set based on pre-cache conditions, not cached size. * @property regY * @type {Number} * @default 0 **/ this.regY = 0; /** * The rotation in degrees for this display object. * @property rotation * @type {Number} * @default 0 **/ this.rotation = 0; /** * The factor to stretch this display object horizontally. For example, setting scaleX to 2 will stretch the display * object to twice its nominal width. To horizontally flip an object, set the scale to a negative number. * @property scaleX * @type {Number} * @default 1 **/ this.scaleX = 1; /** * The factor to stretch this display object vertically. For example, setting scaleY to 0.5 will stretch the display * object to half its nominal height. To vertically flip an object, set the scale to a negative number. * @property scaleY * @type {Number} * @default 1 **/ this.scaleY = 1; /** * The factor to skew this display object horizontally. * @property skewX * @type {Number} * @default 0 **/ this.skewX = 0; /** * The factor to skew this display object vertically. * @property skewY * @type {Number} * @default 0 **/ this.skewY = 0; /** * A shadow object that defines the shadow to render on this display object. Set to `null` to remove a shadow. If * null, this property is inherited from the parent container. * @property shadow * @type {Shadow} * @default null **/ this.shadow = null; /** * Indicates whether this display object should be rendered to the canvas and included when running the Stage * {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}} method. * @property visible * @type {Boolean} * @default true **/ this.visible = true; /** * The x (horizontal) position of the display object, relative to its parent. * @property x * @type {Number} * @default 0 **/ this.x = 0; /** The y (vertical) position of the display object, relative to its parent. * @property y * @type {Number} * @default 0 **/ this.y = 0; /** * If set, defines the transformation for this display object, overriding all other transformation properties * (x, y, rotation, scale, skew). * @property transformMatrix * @type {Matrix2D} * @default null **/ this.transformMatrix = null; /** * The composite operation indicates how the pixels of this display object will be composited with the elements * behind it. If `null`, this property is inherited from the parent container. For more information, read the * <a href="https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-globalcompositeoperation"> * whatwg spec on compositing</a>. For a list of supported compositeOperation value, visit * <a href="https://drafts.fxtf.org/compositing/">the W3C draft on Compositing and Blending</a>. * @property compositeOperation * @type {String} * @default null **/ this.compositeOperation = null; /** * Indicates whether the display object should be drawn to a whole pixel when * {{#crossLink "Stage/snapToPixelEnabled:property"}}{{/crossLink}} is true. To enable/disable snapping on whole * categories of display objects, set this value on the prototype (Ex. Text.prototype.snapToPixel = true). * @property snapToPixel * @type {Boolean} * @default true **/ this.snapToPixel = true; /** * An array of Filter objects to apply to this display object. Filters are only applied / updated when {{#crossLink "cache"}}{{/crossLink}} * or {{#crossLink "updateCache"}}{{/crossLink}} is called on the display object, and only apply to the area that is * cached. * @property filters * @type {Array} * @default null **/ this.filters = null; /** * A Shape instance that defines a vector mask (clipping path) for this display object. The shape's transformation * will be applied relative to the display object's parent coordinates (as if it were a child of the parent). * @property mask * @type {Shape} * @default null */ this.mask = null; /** * A display object that will be tested when checking mouse interactions or testing {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}. * The hit area will have its transformation applied relative to this display object's coordinate space (as though * the hit test object were a child of this display object and relative to its regX/Y). The hitArea will be tested * using only its own `alpha` value regardless of the alpha value on the target display object, or the target's * ancestors (parents). * * If set on a {{#crossLink "Container"}}{{/crossLink}}, children of the Container will not receive mouse events. * This is similar to setting {{#crossLink "mouseChildren"}}{{/crossLink}} to false. * * Note that hitArea is NOT currently used by the `hitTest()` method, nor is it supported for {{#crossLink "Stage"}}{{/crossLink}}. * @property hitArea * @type {DisplayObject} * @default null */ this.hitArea = null; /** * A CSS cursor (ex. "pointer", "help", "text", etc) that will be displayed when the user hovers over this display * object. You must enable mouseover events using the {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}} method to * use this property. Setting a non-null cursor on a Container will override the cursor set on its descendants. * @property cursor * @type {String} * @default null */ this.cursor = null; // private properties: /** * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}} * @property _cacheScale * @protected * @type {Number} * @default 1 * @deprecated **/ /** * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}} * @property _cacheDataURLID * @protected * @type {Number} * @default 0 * @deprecated */ /** * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}} * @property _cacheDataURL * @protected * @type {String} * @default null * @deprecated */ /** * @property _props * @protected * @type {DisplayObject} * @default null **/ this._props = new createjs.DisplayProps(); /** * @property _rectangle * @protected * @type {Rectangle} * @default null **/ this._rectangle = new createjs.Rectangle(); /** * @property _bounds * @protected * @type {Rectangle} * @default null **/ this._bounds = null; /** * Where StageGL should look for required display properties, matters only for leaf display objects. Containers * or cached objects won't use this property, it's for native display of terminal elements. * @property _webGLRenderStyle * @protected * @type {number} * @default 0 */ this._webGLRenderStyle = DisplayObject._StageGL_NONE; } var p = createjs.extend(DisplayObject, createjs.EventDispatcher); // static properties: /** * Listing of mouse event names. Used in _hasMouseEventListener. * @property _MOUSE_EVENTS * @protected * @static * @type {Array} **/ DisplayObject._MOUSE_EVENTS = ["click","dblclick","mousedown","mouseout","mouseover","pressmove","pressup","rollout","rollover"]; /** * Suppresses errors generated when using features like hitTest, mouse events, and {{#crossLink "getObjectsUnderPoint"}}{{/crossLink}} * with cross domain content. * @property suppressCrossDomainErrors * @static * @type {Boolean} * @default false **/ DisplayObject.suppressCrossDomainErrors = false; /** * @property _snapToPixelEnabled * @protected * @static * @type {Boolean} * @default false **/ DisplayObject._snapToPixelEnabled = false; // stage.snapToPixelEnabled is temporarily copied here during a draw to provide global access. /** * Enum like property for determining StageGL render lookup, i.e. where to expect properties. * @property _StageGL_NONE * @protected * @static * @type {number} */ DisplayObject._StageGL_NONE = 0; /** * Enum like property for determining StageGL render lookup, i.e. where to expect properties. * @property _StageGL_SPRITE * @protected * @static * @type {number} */ DisplayObject._StageGL_SPRITE = 1; /** * Enum like property for determining StageGL render lookup, i.e. where to expect properties. * @property _StageGL_BITMAP * @protected * @static * @type {number} */ DisplayObject._StageGL_BITMAP = 2; /** * @property _hitTestCanvas * @type {HTMLCanvasElement | Object} * @static * @protected **/ /** * @property _hitTestContext * @type {CanvasRenderingContext2D} * @static * @protected **/ var canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); // prevent errors on load in browsers without canvas. if (canvas.getContext) { DisplayObject._hitTestCanvas = canvas; DisplayObject._hitTestContext = canvas.getContext("2d"); canvas.width = canvas.height = 1; } // events: /** * Dispatched when the user presses their left mouse button over the display object. See the * {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. * @event mousedown * @since 0.6.0 */ /** * Dispatched when the user presses their left mouse button and then releases it while over the display object. * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. * @event click * @since 0.6.0 */ /** * Dispatched when the user double clicks their left mouse button over this display object. * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. * @event dblclick * @since 0.6.0 */ /** * Dispatched when the user's mouse enters this display object. This event must be enabled using * {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. See also {{#crossLink "DisplayObject/rollover:event"}}{{/crossLink}}. * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. * @event mouseover * @since 0.6.0 */ /** * Dispatched when the user's mouse leaves this display object. This event must be enabled using * {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. See also {{#crossLink "DisplayObject/rollout:event"}}{{/crossLink}}. * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. * @event mouseout * @since 0.6.0 */ /** * This event is similar to {{#crossLink "DisplayObject/mouseover:event"}}{{/crossLink}}, with the following * differences: it does not bubble, and it considers {{#crossLink "Container"}}{{/crossLink}} instances as an * aggregate of their content. * * For example, myContainer contains two overlapping children: shapeA and shapeB. The user moves their mouse over * shapeA and then directly on to shapeB. With a listener for {{#crossLink "mouseover:event"}}{{/crossLink}} on * myContainer, two events would be received, each targeting a child element: * <OL> * <LI>when the mouse enters shapeA (target=shapeA)</LI> * <LI>when the mouse enters shapeB (target=shapeB)</LI> * </OL> * However, with a listener for "rollover" instead, only a single event is received when the mouse first enters * the aggregate myContainer content (target=myContainer). * * This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. * @event rollover * @since 0.7.0 */ /** * This event is similar to {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}, with the following * differences: it does not bubble, and it considers {{#crossLink "Container"}}{{/crossLink}} instances as an * aggregate of their content. * * For example, myContainer contains two overlapping children: shapeA and shapeB. The user moves their mouse over * shapeA, then directly on to shapeB, then off both. With a listener for {{#crossLink "mouseout:event"}}{{/crossLink}} * on myContainer, two events would be received, each targeting a child element: * <OL> * <LI>when the mouse leaves shapeA (target=shapeA)</LI> * <LI>when the mouse leaves shapeB (target=shapeB)</LI> * </OL> * However, with a listener for "rollout" instead, only a single event is received when the mouse leaves * the aggregate myContainer content (target=myContainer). * * This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. * @event rollout * @since 0.7.0 */ /** * After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressmove * event will be generated on that object whenever the mouse moves until the mouse press is released. This can be * useful for dragging and similar operations. * * **Please note** that if the initial mouse target from a `mousedown` event is removed from the stage after being pressed * (e.g. during a `pressmove` event), a `pressmove` event is still generated. However since it is no longer in the * display list, the event can not bubble. This means that previous ancestors (parent containers) will not receive * the event, and therefore can not re-dispatch it. If you intend to listen for `{{#crossLink "DisplayObject/pressup:event"}}{{/crossLink}}` * or `pressmove` on a dynamic object (such as a {{#crossLink "MovieClip"}}{{/crossLink}} or {{#crossLink "Container"}}{{/crossLink}}), * then ensure you set {{#crossLink "Container/mouseChildren:property"}}{{/crossLink}} to `false`. * @event pressmove * @since 0.7.0 */ /** * After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressup event * will be generated on that object when that mouse press is released. This can be useful for dragging and similar * operations. * * **Please note** that if the initial mouse target from a `mousedown` event is removed from the stage after being pressed * (e.g. during a `pressmove` event), a `pressup` event is still generated. However since it is no longer in the * display list, the event can not bubble. This means that previous ancestors (parent containers) will not receive * the event, and therefore can not re-dispatch it. If you intend to listen for `{{#crossLink "DisplayObject/pressmove:event"}}{{/crossLink}}` * or `pressup` on a dynamic object (such as a {{#crossLink "MovieClip"}}{{/crossLink}} or {{#crossLink "Container"}}{{/crossLink}}), * then ensure you set {{#crossLink "Container/mouseChildren:property"}}{{/crossLink}} to `false`. * @event pressup * @since 0.7.0 */ /** * Dispatched when the display object is added to a parent container. * @event added */ /** * Dispatched when the display object is removed from its parent container. * @event removed */ /** * Dispatched on each display object on a stage whenever the stage updates. This occurs immediately before the * rendering (draw) pass. When {{#crossLink "Stage/update"}}{{/crossLink}} is called, first all display objects on * the stage dispatch the tick event, then all of the display objects are drawn to stage. Children will have their * {{#crossLink "tick:event"}}{{/crossLink}} event dispatched in order of their depth prior to the event being * dispatched on their parent. * @event tick * @param {Object} target The object that dispatched the event. * @param {String} type The event type. * @param {Array} params An array containing any arguments that were passed to the Stage.update() method. For * example if you called stage.update("hello"), then the params would be ["hello"]. * @since 0.6.0 */ // getter / setters: /** * Use the {{#crossLink "DisplayObject/stage:property"}}{{/crossLink}} property instead. * @method _getStage * @protected * @return {Stage} **/ p._getStage = function() { // uses dynamic access to avoid circular dependencies; var o = this, _Stage = createjs["Stage"]; while (o.parent) { o = o.parent; } if (o instanceof _Stage) { return o; } return null; }; /** * Use the {{#crossLink "DisplayObject/stage:property"}}{{/crossLink}} property instead. * @method getStage * @deprecated */ // DisplayObject.getStage is @deprecated. Remove for 1.1+ p.getStage = createjs.deprecate(p._getStage, "DisplayObject.getStage"); /** * Returns the Stage instance that this display object will be rendered on, or null if it has not been added to one. * @property stage * @type {Stage} * @readonly **/ /** * Returns an ID number that uniquely identifies the current cache for this display object. This can be used to * determine if the cache has changed since a previous check. * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}} * @property cacheID * @deprecated * @type {Number} * @default 0 */ /** * Set both the {{#crossLink "DisplayObject/scaleX:property"}}{{/crossLink}} and the {{#crossLink "DisplayObject/scaleY:property"}}{{/crossLink}} * property to the same value. Note that when you get the value, if the `scaleX` and `scaleY` are different values, * it will return only the `scaleX`. * @property scale * @type {Number} * @default 1 */ try { Object.defineProperties(p, { stage: { get: p._getStage }, cacheID: { get: function(){ return this.bitmapCache && this.bitmapCache.cacheID }, set: function(a){ this.bitmapCache && (this.bitmapCache.cacheID = a) } }, scale: { get: function() { return this.scaleX; }, set: function(scale) { this.scaleX = this.scaleY = scale; } } }); } catch (e) {} // 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() { return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0); }; /** * Draws the display object into the specified context ignoring its 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=false] 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). * @return {Boolean} **/ p.draw = function(ctx, ignoreCache) { var cache = this.bitmapCache; if(cache && !ignoreCache) { return cache.draw(ctx); } return false; }; /** * Applies this display object's transformation, alpha, globalCompositeOperation, clipping path (mask), and shadow * to the specified context. This is typically called prior to {{#crossLink "DisplayObject/draw"}}{{/crossLink}}. * @method updateContext * @param {CanvasRenderingContext2D} ctx The canvas 2D to update. **/ p.updateContext = function(ctx) { var o=this, mask=o.mask, mtx= o._props.matrix; if (mask && mask.graphics && !mask.graphics.isEmpty()) { mask.getMatrix(mtx); ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty); mask.graphics.drawAsPath(ctx); ctx.clip(); mtx.invert(); ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty); } this.getMatrix(mtx); var tx = mtx.tx, ty = mtx.ty; if (DisplayObject._snapToPixelEnabled && o.snapToPixel) { tx = tx + (tx < 0 ? -0.5 : 0.5) | 0; ty = ty + (ty < 0 ? -0.5 : 0.5) | 0; } ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, tx, ty); ctx.globalAlpha *= o.alpha; if (o.compositeOperation) { ctx.globalCompositeOperation = o.compositeOperation; } if (o.shadow) { this._applyShadow(ctx, o.shadow); } }; /** * Draws the display object into a new element, which is then used for subsequent draws. Intended for complex content * that does not change frequently (ex. a Container with many children that do not move, or a complex vector Shape), * this can provide for much faster rendering because the content does not need to be re-rendered each tick. The * cached display object can be moved, rotated, faded, etc freely, however if its content changes, you must manually * update the cache by calling `updateCache()` again. You must specify the cached area via the x, y, w, and h * parameters. This defines the rectangle that will be rendered and cached using this display object's coordinates. * * <h4>Example</h4> * For example if you defined a Shape that drew a circle at 0, 0 with a radius of 25: * * var shape = new createjs.Shape(); * shape.graphics.beginFill("#ff0000").drawCircle(0, 0, 25); * shape.cache(-25, -25, 50, 50); * * Note that filters need to be defined <em>before</em> the cache is applied or you will have to call updateCache after * application. Check out the {{#crossLink "Filter"}}{{/crossLink}} class for more information. Some filters * (ex. BlurFilter) may not work as expected in conjunction with the scale param. * * Usually, the resulting cacheCanvas will have the dimensions width * scale, height * scale, however some filters (ex. BlurFilter) * will add padding to the canvas dimensions. * * In previous versions caching was handled on DisplayObject but has since been moved to {{#crossLink "BitmapCache"}}{{/crossLink}}. * This allows for easier interaction and alternate cache methods like WebGL with {{#crossLink "StageGL"}}{{/crossLink}}. * For more information on the options object, see the BitmapCache {{#crossLink "BitmapCache/define"}}{{/crossLink}}. * * @method cache * @param {Number} x The x coordinate origin for the cache region. * @param {Number} y The y coordinate origin for the cache region. * @param {Number} width The width of the cache region. * @param {Number} height The height of the cache region. * @param {Number} [scale=1] The scale at which the cache will be created. For example, if you cache a vector shape using * myShape.cache(0,0,100,100,2) then the resulting cacheCanvas will be 200x200 px. This lets you scale and rotate * cached elements with greater fidelity. Default is 1. * @param {Object} [options=undefined] Specify additional parameters for the cache logic **/ p.cache = function(x, y, width, height, scale, options) { if(!this.bitmapCache){ this.bitmapCache = new createjs.BitmapCache(); } this.bitmapCache.define(this, x, y, width, height, scale, options); }; /** * Redraws the display object to its cache. Calling updateCache without an active cache will throw an error. * If compositeOperation is null the current cache will be cleared prior to drawing. Otherwise the display object * will be drawn over the existing cache using the specified compositeOperation. * * <h4>Example</h4> * Clear the current graphics of a cached shape, draw some new instructions, and then update the cache. The new line * will be drawn on top of the old one. * * // Not shown: Creating the shape, and caching it. * shapeInstance.clear(); * shapeInstance.setStrokeStyle(3).beginStroke("#ff0000").moveTo(100, 100).lineTo(200,200); * shapeInstance.updateCache(); * * In previous versions caching was handled on DisplayObject but has since been moved to {{#crossLink "BitmapCache"}}{{/crossLink}}. * This allows for easier interaction and alternate cache methods like WebGL and {{#crossLink "StageGL"}}{{/crossLink}}. * * @method updateCache * @param {String} compositeOperation The compositeOperation to use, or null to clear the cache and redraw it. * <a href="https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-globalcompositeoperation"> * whatwg spec on compositing</a>. **/ p.updateCache = function(compositeOperation) { if(!this.bitmapCache) { throw "cache() must be called before updateCache()"; } this.bitmapCache.update(compositeOperation); }; /** * Clears the current cache. See {{#crossLink "DisplayObject/cache"}}{{/crossLink}} for more information. * @method uncache **/ p.uncache = function() { if(this.bitmapCache) { this.bitmapCache.release(); this.bitmapCache = undefined; } }; /** * Returns a data URL for the cache, or null if this display object is not cached. * Only generated if the cache has changed, otherwise returns last result. * @method getCacheDataURL * @return {String} The image data url for the cache. **/ p.getCacheDataURL = function() { return this.bitmapCache?this.bitmapCache.getDataURL():null; }; /** * Transforms the specified x and y position from the coordinate space of the display object * to the global (stage) coordinate space. For example, this could be used to position an HTML label * over a specific point on a nested display object. Returns a Point instance with x and y properties * correlating to the transformed coordinates on the stage. * * <h4>Example</h4> * * displayObject.x = 300; * displayObject.y = 200; * stage.addChild(displayObject); * var point = displayObject.localToGlobal(100, 100); * // Results in x=400, y=300 * * @method localToGlobal * @param {Number} x The x position in the source display object to transform. * @param {Number} y The y position in the source display object to transform. * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned. * @return {Point} A Point instance with x and y properties correlating to the transformed coordinates * on the stage. **/ p.localToGlobal = function(x, y, pt) { return this.getConcatenatedMatrix(this._props.matrix).transformPoint(x,y, pt||new createjs.Point()); }; /** * Transforms the specified x and y position from the global (stage) coordinate space to the * coordinate space of the display object. For example, this could be used to determine * the current mouse position within the display object. Returns a Point instance with x and y properties * correlating to the transformed position in the display object's coordinate space. * * <h4>Example</h4> * * displayObject.x = 300; * displayObject.y = 200; * stage.addChild(displayObject); * var point = displayObject.globalToLocal(100, 100); * // Results in x=-200, y=-100 * * @method globalToLocal * @param {Number} x The x position on the stage to transform. * @param {Number} y The y position on the stage to transform. * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned. * @return {Point} A Point instance with x and y properties correlating to the transformed position in the * display object's coordinate space. **/ p.globalToLocal = function(x, y, pt) { return this.getConcatenatedMatrix(this._props.matrix).invert().transformPoint(x,y, pt||new createjs.Point()); }; /** * Transforms the specified x and y position from the coordinate space of this display object to the coordinate * space of the target display object. Returns a Point instance with x and y properties correlating to the * transformed position in the target's coordinate space. Effectively the same as using the following code with * {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}} and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}. * * var pt = this.localToGlobal(x, y); * pt = target.globalToLocal(pt.x, pt.y); * * @method localToLocal * @param {Number} x The x position in the source display object to transform. * @param {Number} y The y position on the source display object to transform. * @param {DisplayObject} target The target display object to which the coordinates will be transformed. * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned. * @return {Point} Returns a Point instance with x and y properties correlating to the transformed position * in the target's coordinate space. **/ p.localToLocal = function(x, y, target, pt) { pt = this.localToGlobal(x, y, pt); return target.globalToLocal(pt.x, pt.y, pt); }; /** * Shortcut method to quickly set the transform properties on the display object. All parameters are optional. * Omitted parameters will have the default value set. * * <h4>Example</h4> * * displayObject.setTransform(100, 100, 2, 2); * * @method setTransform * @param {Number} [x=0] The horizontal translation (x position) in pixels * @param {Number} [y=0] The vertical translation (y position) in pixels * @param {Number} [scaleX=1] The horizontal scale, as a percentage of 1 * @param {Number} [scaleY=1] the vertical scale, as a percentage of 1 * @param {Number} [rotation=0] The rotation, in degrees * @param {Number} [skewX=0] The horizontal skew factor * @param {Number} [skewY=0] The vertical skew factor * @param {Number} [regX=0] The horizontal registration point in pixels * @param {Number} [regY=0] The vertical registration point in pixels * @return {DisplayObject} Returns this instance. Useful for chaining commands. * @chainable */ p.setTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) { this.x = x || 0; this.y = y || 0; this.scaleX = scaleX == null ? 1 : scaleX; this.scaleY = scaleY == null ? 1 : scaleY; this.rotation = rotation || 0; this.skewX = skewX || 0; this.skewY = skewY || 0; this.regX = regX || 0; this.regY = regY || 0; return this; }; /** * Returns a matrix based on this object's current transform. * @method getMatrix * @param {Matrix2D} matrix Optional. A Matrix2D object to populate with the calculated values. If null, a new * Matrix object is returned. * @return {Matrix2D} A matrix representing this display object's transform. **/ p.getMatrix = function(matrix) { var o = this, mtx = matrix&&matrix.identity() || new createjs.Matrix2D(); return o.transformMatrix ? mtx.copy(o.transformMatrix) : mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY); }; /** * Generates a Matrix2D object representing the combined transform of the display object and all of its * parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}). This can * be used to transform positions between coordinate spaces, such as with {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}} * and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}. * @method getConcatenatedMatrix * @param {Matrix2D} [matrix] A {{#crossLink "Matrix2D"}}{{/crossLink}} object to populate with the calculated values. * If null, a new Matrix2D object is returned. * @return {Matrix2D} The combined matrix. **/ p.getConcatenatedMatrix = function(matrix) { var o = this, mtx = this.getMatrix(matrix); while (o = o.parent) { mtx.prependMatrix(o.getMatrix(o._props.matrix)); } return mtx; }; /** * Generates a DisplayProps object representing the combined display properties of the object and all of its * parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}). * @method getConcatenatedDisplayProps * @param {DisplayProps} [props] A {{#crossLink "DisplayProps"}}{{/crossLink}} object to populate with the calculated values. * If null, a new DisplayProps object is returned. * @return {DisplayProps} The combined display properties. **/ p.getConcatenatedDisplayProps = function(props) { props = props ? props.identity() : new createjs.DisplayProps(); var o = this, mtx = o.getMatrix(props.matrix); do { props.prepend(o.visible, o.alpha, o.shadow, o.compositeOperation); // we do this to avoid problems with the matrix being used for both operations when o._props.matrix is passed in as the props param. // this could be simplified (ie. just done as part of the prepend above) if we switched to using a pool. if (o != this) { mtx.prependMatrix(o.getMatrix(o._props.matrix)); } } while (o = o.parent); return props; }; /** * Tests whether the display object intersects the specified point in local coordinates (ie. draws a pixel with alpha > 0 at * the specified position). This ignores the alpha, shadow, hitArea, mask, and compositeOperation of the display object. * * <h4>Example</h4> * * stage.addEventListener("stagemousedown", handleMouseDown); * function handleMouseDown(event) { * var hit = myShape.hitTest(event.stageX, event.stageY); * } * * Please note that shape-to-shape collision is not currently supported by EaselJS. * @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 a visible portion of the DisplayObject intersect the specified * local Point. */ p.hitTest = function(x, y) { var ctx = DisplayObject._hitTestContext; ctx.setTransform(1, 0, 0, 1, -x, -y); this.draw(ctx); var hit = this._testHit(ctx); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.clearRect(0, 0, 2, 2); return hit; }; /** * Provides a chainable shortcut method for setting a number of properties on the instance. * * <h4>Example</h4> * * var myGraphics = new createjs.Graphics().beginFill("#ff0000").drawCircle(0, 0, 25); * var shape = stage.addChild(new Shape()).set({graphics:myGraphics, x:100, y:100, alpha:0.5}); * * @method set * @param {Object} props A generic object containing properties to copy to the DisplayObject instance. * @return {DisplayObject} Returns the instance the method is called on (useful for chaining calls.) * @chainable */ p.set = function(props) { for (var n in props) { this[n] = props[n]; } return this; }; /** * Returns a rectangle representing this object's bounds in its local coordinate system (ie. with no transformation). * Objects that have been cached will return the bounds of the cache. * * Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use * {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container * bounds. * * <table> * <tr><td><b>All</b></td><td> * All display objects support setting bounds manually using setBounds(). Likewise, display objects that * have been cached using cache() will return the bounds of their cache. Manual and cache bounds will override * the automatic calculations listed below. * </td></tr> * <tr><td><b>Bitmap</b></td><td> * Returns the width and height of the {{#crossLink "Bitmap/sourceRect"}}{{/crossLink}} (if specified) or image, * extending from (x=0,y=0). * </td></tr> * <tr><td><b>Sprite</b></td><td> * Returns the bounds of the current frame. May have non-zero x/y if a frame registration point was specified * in the spritesheet data. See also {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}} * </td></tr> * <tr><td><b>Container</b></td><td> * Returns the aggregate (combined) bounds of all children that return a non-null value from `getBounds()`. * </td></tr> * <tr><td><b>Shape</b></td><td> * Does not currently support automatic bounds calculations. Use `setBounds()` to manually define bounds. * </td></tr> * <tr><td><b>Text</b></td><td> * Returns approximate bounds. Horizontal values (x/width) are quite accurate, but vertical values (y/height) * are not, especially when using {{#crossLink "Text/textBaseline:property"}}{{/crossLink}} values other than "top". * </td></tr> * <tr><td><b>BitmapText</b></td><td> * Returns approximate bounds. Values will be more accurate if spritesheet frame registration points are close * to (x=0,y=0). * </td></tr> * </table> * * Bounds can be expensive to calculate for some objects (ex. text, or containers with many children), and * are recalculated each time you call getBounds(). You can prevent recalculation on static objects by setting the * bounds explicitly: * * var bounds = obj.getBounds(); * obj.setBounds(bounds.x, bounds.y, bounds.width, bounds.height); * // getBounds will now use the set values, instead of recalculating * * To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its * values if you need to retain it. * * var myBounds = obj.getBounds().clone(); * // OR: * myRect.copy(obj.getBounds()); * * @method getBounds * @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this * object. **/ p.getBounds = function() { if (this._bounds) { return this._rectangle.copy(this._bounds); } var cache = this.bitmapCache; if (cache) { return cache.getBounds(); } return null; }; /** * Returns a rectangle representing this object's bounds in its parent's coordinate system (ie. with transformations applied). * Objects that have been cached will return the transformed bounds of the cache. * * Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use * {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container * bounds. * * To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its * values if you need to retain it. * * Container instances calculate aggregate bounds for all children that return bounds via getBounds. * @method getTransformedBounds * @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this object. **/ p.getTransformedBounds = function() { return this._getBounds(); }; /** * Allows you to manually specify the bounds of an object that either cannot calculate their own bounds (ex. Shape & * Text) for future reference, or so the object can be included in Container bounds. Manually set bounds will always * override calculated bounds. * * The bounds should be specified in the object's local (untransformed) coordinates. For example, a Shape instance * with a 25px radius circle centered at 0,0 would have bounds of (-25, -25, 50, 50). * @method setBounds * @param {Number} x The x origin of the bounds. Pass null to remove the manual bounds. * @param {Number} y The y origin of the bounds. * @param {Number} width The width of the bounds. * @param {Number} height The height of the bounds. **/ p.setBounds = function(x, y, width, height) { if (x == null) { this._bounds = x; return; } this._bounds = (this._bounds || new createjs.Rectangle()).setValues(x, y, width, height); }; /** * Returns a clone of this DisplayObject. Some properties that are specific to this instance's current context are * reverted to their defaults (for example .parent). Caches are not maintained across clones, and some elements * are copied by reference (masks, individual filter instances, hit area) * @method clone * @return {DisplayObject} A clone of the current DisplayObject instance. **/ p.clone = function() { return this._cloneProps(new DisplayObject()); }; /** * Returns a string representation of this object. * @method toString * @return {String} a string representation of the instance. **/ p.toString = function() { return "[DisplayObject (name="+ this.name +")]"; }; // private methods: /** * Called before the object gets drawn and is a chance to ensure the display state of the object is correct. * Mostly used by {{#crossLink "MovieClip"}}{{/crossLink}} and {{#crossLink "BitmapText"}}{{/crossLink}} to * correct their internal state and children prior to being drawn. * * Is manually called via draw in a {{#crossLink "Stage"}}{{/crossLink}} but is automatically called when * present in a {{#crossLink "StageGL"}}{{/crossLink}} instance. * * @method _updateState * @protected * @default null */ p._updateState = null; // separated so it can be used more easily in subclasses: /** * @method _cloneProps * @param {DisplayObject} o The DisplayObject instance which will have properties from the current DisplayObject * instance copied into. * @return {DisplayObject} o * @protected **/ p._cloneProps = function(o) { o.alpha = this.alpha; o.mouseEnabled = this.mouseEnabled; o.tickEnabled = this.tickEnabled; o.name = this.name; o.regX = this.regX; o.regY = this.regY; o.rotation = this.rotation; o.scaleX = this.scaleX; o.scaleY = this.scaleY; o.shadow = this.shadow; o.skewX = this.skewX; o.skewY = this.skewY; o.visible = this.visible; o.x = this.x; o.y = this.y; o.compositeOperation = this.compositeOperation; o.snapToPixel = this.snapToPixel; o.filters = this.filters==null?null:this.filters.slice(0); o.mask = this.mask; o.hitArea = this.hitArea; o.cursor = this.cursor; o._bounds = this._bounds; return o; }; /** * @method _applyShadow * @protected * @param {CanvasRenderingContext2D} ctx * @param {Shadow} shadow **/ p._applyShadow = function(ctx, shadow) { shadow = shadow || Shadow.identity; ctx.shadowColor = shadow.color; ctx.shadowOffsetX = shadow.offsetX; ctx.shadowOffsetY = shadow.offsetY; ctx.shadowBlur = shadow.blur; }; /** * @method _tick * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs. * @protected **/ p._tick = function(evtObj) { // because tick can be really performance sensitive, check for listeners before calling dispatchEvent. var ls = this._listeners; if (ls && ls["tick"]) { // reset & reuse the event object to avoid construction / GC costs: evtObj.target = null; evtObj.propagationStopped = evtObj.immediatePropagationStopped = false; this.dispatchEvent(evtObj); } }; /** * @method _testHit * @protected * @param {CanvasRenderingContext2D} ctx * @return {Boolean} **/ p._testHit = function(ctx) { try { var hit = ctx.getImageData(0, 0, 1, 1).data[3] > 1; } catch (e) { if (!DisplayObject.suppressCrossDomainErrors) { throw "An error has occurred. This is most likely due to security restrictions on reading canvas pixel data with local or cross-domain images."; } } return hit; }; /** * @method _getBounds * @param {Matrix2D} matrix * @param {Boolean} ignoreTransform If true, does not apply this object's transform. * @return {Rectangle} * @protected **/ p._getBounds = function(matrix, ignoreTransform){ return this._transformBounds(this.getBounds(), matrix, ignoreTransform); }; /** * @method _transformBounds * @param {Rectangle} bounds * @param {Matrix2D} matrix * @param {Boolean} ignoreTransform * @return {Rectangle} * @protected **/ p._transformBounds = function(bounds, matrix, ignoreTransform) { if (!bounds) { return bounds; } var x = bounds.x, y = bounds.y, width = bounds.width, height = bounds.height, mtx = this._props.matrix; mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx); if (x || y) { mtx.appendTransform(0,0,1,1,0,0,0,-x,-y); } // TODO: simplify this. if (matrix) { mtx.prependMatrix(matrix); } var x_a = width*mtx.a, x_b = width*mtx.b; var y_c = height*mtx.c, y_d = height*mtx.d; var tx = mtx.tx, ty = mtx.ty; var minX = tx, maxX = tx, minY = ty, maxY = ty; if ((x = x_a + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; } if ((x = x_a + y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; } if ((x = y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; } if ((y = x_b + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; } if ((y = x_b + y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; } if ((y = y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; } return bounds.setValues(minX, minY, maxX-minX, maxY-minY); }; /** * Indicates whether the display object has any mouse event listeners or a cursor. * @method _isMouseOpaque * @return {Boolean} * @protected **/ p._hasMouseEventListener = function() { var evts = DisplayObject._MOUSE_EVENTS; for (var i= 0, l=evts.length; i<l; i++) { if (this.hasEventListener(evts[i])) { return true; } } return !!this.cursor; }; createjs.DisplayObject = createjs.promote(DisplayObject, "EventDispatcher"); }());