1/**
2 * DisplayObject by Grant Skinner. Dec 5, 2010
3 * Visit http://easeljs.com/ for documentation, updates and examples.
4 *
5 *
6 * Copyright (c) 2010 Grant Skinner
7 *
8 * Permission is hereby granted, free of charge, to any person
9 * obtaining a copy of this software and associated documentation
10 * files (the "Software"), to deal in the Software without
11 * restriction, including without limitation the rights to use,
12 * copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following
15 * conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 */ 29 30 31 32/**
33 * DisplayObject is an abstract class that should not be constructed directly. Instead construct subclasses such as Sprite, Bitmap, and Shape.
34 * @class DisplayObject is the base class for all display classes in the CanvasDisplay library. It defines the core properties and methods that are shared between all display objects. It should not be instantiated directly.
35 */ 36functionDisplayObject(){ 37this.initialize(); 38this.prototype; 39 40/** Suppresses errors generated when using features like hitTest, onPress/onClick, and getObjectsUnderPoint with cross domain content. */ 41DisplayObject.suppressCrossDomainErrors=false; 42 43/** @private */ 44DisplayObject._hitTestCanvas=document.createElement("canvas"); 45DisplayObject._hitTestCanvas.width=DisplayObject._hitTestCanvas.height=1; 46/** @private */ 47DisplayObject._hitTestContext=DisplayObject._hitTestCanvas.getContext("2d"); 48/** @private */ 49DisplayObject._workingMatrix=newMatrix2D(); 50 51// public properties: 52/** The alpha (transparency) for this display object. 0 is fully transparent, 1 is fully opaque. */ 53this.alpha=1; 54/** If a cache is active, this returns the canvas that holds the cached version of this display object. See cache() for more information. READ-ONLY. */ 55this.cacheCanvas=null; 56/** Unique ID for this display object. Makes display objects easier for some uses. */ 57this.id=-1; 58/** Indicates whether to include this object when running Stage.getObjectsUnderPoint(). Setting this to true for Sprites will cause the Sprite to be returned (not its children) regardless of whether it's mouseChildren property is true. */ 59this.mouseEnabled=true; 60/** An optional name for this display object. Included in toString(). Useful for debugging. */ 61this.name=null; 62/** A reference to the Sprite or Stage object that contains this display object, or null if it has not been added to one. READ-ONLY. */ 63this.parent=null; 64/** The x offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate around it's center, you would set regX and regY to 50. */ 65this.regX=0; 66/** The y offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate around it's center, you would set regX and regY to 50. */ 67this.regY=0; 68/** The rotation in degrees for this display object. */ 69this.rotation=0; 70/** The factor to stretch this display object horizontally. For example, setting scaleX to 2 will stretch the display object to twice it's nominal width. */ 71this.scaleX=1; 72/** The factor to stretch this display object vertically. For example, setting scaleY to 0.5 will stretch the display object to half it's nominal height. */ 73this.scaleY=1; 74/** The factor to skew this display object horizontally. */ 75this.skewX=0; 76/** The factor to skew this display object vertically. */ 77this.skewY=0; 78/** 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. */ 79this.shadow=null; 80/** Indicates whether this display object should be rendered to the canvas and included when running Stage.getObjectsUnderPoint(). */ 81this.visible=true; 82/** The x (horizontal) position of the display object, relative to its parent. */ 83this.x=0; 84/** The y (vertical) position of the display object, relative to its parent. */ 85this.y=0; 86/** 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="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#compositing">whatwg spec on compositing</a>. */ 87this.compositeOperation=null; 88/** Indicates whether the display object should have it's x & y position rounded prior to drawing it to stage. This only applies if the enclosing stage has snapPixelsEnabled set to true, and the display object's composite transform does not include any scaling, rotation, or skewing. The snapToPixel property is true by default for Bitmap and BitmapSequence instances, and false for all other display objects. */ 89this.snapToPixel=false; 90/** The onPress callback is called when the user presses down on their mouse over this display object. The handler is passed a single param containing the corresponding MouseEvent instance. You can subscribe to the onMouseMove and onMouseUp callbacks of the event object to receive these events until the user releases the mouse button. If an onPress handler is set on a container, it will receive the event if any of its children are clicked. */ 91this.onPress=null; 92/** The onClick callback is called when the user presses down on and then releases the mouse button over this display object. The handler is passed a single param containing the corresponding MouseEvent instance. If an onClick handler is set on a container, it will receive the event if any of its children are clicked. */ 93this.onClick=null; 94 95// private properties: 96/** @private */ 97this._cacheOffsetX=0; 98/** @private */ 99this._cacheOffsetY=0;100/** @private */101this._cacheDraw=false;102/** @private */103this._activeContext=null;104/** @private */105this._restoreContext=false;106/** @private */107this._revertShadow=false;108/** @private */109this._revertX=0;110/** @private */111this._revertY=0;112/** @private */113this._revertAlpha=1;114115// constructor:116// separated so it can be easily addressed in subclasses:117/** @private */118this.initialize=function(){119this.id=UID.get();120this.children=[];121}122123// public methods:124/**
125 * NOTE: This method is mainly for internal use, though it may be useful for advanced developers.
126 * Returns true or falsee indicating whether the display object would be visible if drawn to a canvas.
127 * This does not account for whether it would be visible within the boundaries of the stage.
128 */129this.isVisible=function(){130returnthis.visible&&this.alpha>0&&this.scaleX!=0&&this.scaleY!=0;131}132133/**
134 * NOTE: This method is mainly for internal use, though it may be useful for advanced developers.
135 * Draws the display object into the specified context ignoring it's visible, alpha, shadow, and transform.
136 * Returns true if the draw was handled (useful for overriding functionality).
137 * @param ctx The canvas 2D context object to draw into.
138 * @param ignoreCache Indicates whether the draw operation should ignore any current cache. For example,
139 * used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
140 */141this.draw=function(ctx,ignoreCache){142if(ignoreCache||!this.cacheCanvas){returnfalse;}143ctx.translate(this._cacheOffsetX,this._cacheOffsetY);144ctx.drawImage(this.cacheCanvas,0,0);145ctx.translate(-this._cacheOffsetX,-this._cacheOffsetY);146returntrue;147}148149/**
150 * Draws the display object into a new canvas, which is then used for subsequent draws. For complex content that does not change frequently (ex. a Sprite 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 it's content changes, you must manually update the cache by calling updateCache() or cache() again. You must specify the cache 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. For example if you defined a Shape that drew a circle at 0,0 with a radius of 25, you could call myShape.cache(-25,-25,50,50) to cache the full shape.
151 * @param x
152 * @param y
153 * @param width
154 * @param height
155 */156this.cache=function(x,y,width,height){157// draw to canvas.158varctx;159if(this.cacheCanvas==null){this.cacheCanvas=document.createElement("canvas");}160ctx=this.cacheCanvas.getContext("2d");161this.cacheCanvas.width=width;162this.cacheCanvas.height=height;163ctx.setTransform(1,0,0,1,-x,-y);164ctx.clearRect(0,0,width+1,height+1);// because some browsers don't correctly clear if the width/height remain the same.165this.draw(ctx,true);166this._cacheOffsetX=x;167this._cacheOffsetY=y;168}169170/**
171 * Redraws the display object to its cache. Calling updateCache without an active cache will throw an error.
172 * If compositeOperation is null the current cache will be cleared prior to drawing. Otherwise the display object
173 * will be drawn over the existing cache using the specified compositeOperation.
174 * @param compositeOperation The compositeOperation to use, or null to clear the cache and redraw it. <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#compositing">whatwg spec on compositing</a>.
175 */176this.updateCache=function(compositeOperation){177if(this.cacheCanvas==null){throw"cache() must be called before updateCache()";}178ctx=this.cacheCanvas.getContext("2d");179ctx.setTransform(1,0,0,1,-this._cacheOffsetX,-this._cacheOffsetY);180if(!compositeOperation){ctx.clearRect(0,0,this.cacheCanvas.width+1,this.cacheCanvas.height+1);}181else{ctx.globalCompositeOperation=compositeOperation;}182this.draw(ctx,true);183if(compositeOperation){ctx.globalCompositeOperation="source-over";}184}185186/**
187 * Clears the current cache. See cache() for more information.
188 */189this.uncache=function(){190this.cacheCanvas=null;191this.cacheOffsetX=this.cacheOffsetY=0;192}193194/**
195 * Returns the stage that this display object will be rendered on, or null if it has not been added to one.
196 */197this.getStage=function(){198varo=this;199while(o.parent){200o=o.parent;201}202if(oinstanceofStage){returno;}203returnnull;204}205206/**
207 * Transforms the specified x and y position from the coordinate space of the display object
208 * to the global (stage) coordinate space. For example, this could be used to position an HTML label
209 * over a specific point on a nested display object. Returns a Point instance with x and y properties
210 * correlating to the transformed coordinates on the stage.
211 * @param x The x position in the source display object to transform.
212 * @param y The y position in the source display object to transform.
213 */214this.localToGlobal=function(x,y){215varmtx=this.getConcatenatedMatrix();216if(mtx==null){returnnull;}217mtx.append(1,0,0,1,x,y);218returnnewPoint(mtx.tx,mtx.ty);219}220221/**
222 * Transforms the specified x and y position from the global (stage) coordinate space to the
223 * coordinate space of the display object. For example, this could be used to determine
224 * the current mouse position within the display object. Returns a Point instance with x and y properties
225 * correlating to the transformed position in the display object's coordinate space.
226 * @param x The x position on the stage to transform.
227 * @param y The y position on the stage to transform.
228 */229this.globalToLocal=function(x,y){230varmtx=this.getConcatenatedMatrix();231if(mtx==null){returnnull;}232mtx.invert();233mtx.append(1,0,0,1,x,y);234returnnewPoint(mtx.tx,mtx.ty);235}236237/**
238 * Transforms the specified x and y position from the coordinate space of this display object to the
239 * coordinate space of the target display object. Returns a Point instance with x and y properties
240 * correlating to the transformed position in the target's coordinate space. Effectively the same as calling
241 * var pt = this.localToGlobal(x, y); pt = target.globalToLocal(pt.x, pt.y);
242 * @param x The x position in the source display object to transform.
243 * @param y The y position on the stage to transform.
244 * @param target The target display object to which the coordinates will be transformed.
245 */246this.localToLocal=function(x,y,target){247varpt=this.localToGlobal(x,y);248returntarget.globalToLocal(pt.x,pt.y);249}250251/**
252 * Generates a concatenated Matrix2D object representing the combined transform of
253 * the display object and all of its parent Containers up to the highest level ancestor
254 * (usually the stage). This can be used to transform positions between coordinate spaces,
255 * such as with localToGlobal and globalToLocal.
256 * @param mtx Optional. A Matrix2D object to populate with the calculated values. If null, a new Matrix object is returned.
257 */258this.getConcatenatedMatrix=function(mtx){259if(mtx){mtx.identity();}260else{mtx=newMatrix2D();}261vartarget=this;262while(true){263mtx.prependTransform(target.x,target.y,target.scaleX,target.scaleY,target.rotation,target.skewX,target.skewY,target.regX,target.regY);264mtx.prependProperties(target.alpha,target.shadow,target.compositeOperation);265if((p=target.parent)==null){break;}266target=p;267}268returnmtx;269}270271/**
272 * Tests whether the display object intersects the specified local point (ie. draws a pixel with alpha > 0 at the specified position).
273 * This ignores the alpha, shadow and compositeOperation of the display object, and all transform properties including regX/Y.
274 * @param x The x position to check in the display object's local coordinates.
275 * @param y The y position to check in the display object's local coordinates.
276 */277this.hitTest=function(x,y){278varctx=DisplayObject._hitTestContext;279varcanvas=DisplayObject._hitTestCanvas;280281ctx.setTransform(1,0,0,1,-x,-y);282this.draw(ctx);283284varhit=this._testHit(ctx);285286canvas.width=0;287canvas.width=1;288returnhit;289}290291/**
292 * 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).
293 */294this.clone=function(){295varo=newDisplayObject();296this.cloneProps(o);297returno;298}299300/**
301 * Returns a string representation of this object.
302 */303this.toString=function(){304return"[DisplayObject (name="+this.name+")]";305}306307// private methods:308309// separated so it can be used more easily in subclasses:310/** @private */311this.cloneProps=function(o){312o.alpha=this.alpha;313o.name=this.name;314o.regX=this.regX;315o.regY=this.regY;316o.rotation=this.rotation;317o.scaleX=this.scaleX;318o.scaleY=this.scaleY;319o.shadow=this.shadow;320o.skewX=this.skewX;321o.skewY=this.skewY;322o.visible=this.visible;323o.x=this.x;324o.y=this.y;325o.mouseEnabled=this.mouseEnabled;326o.compositeOperation=this.compositeOperation;327}328329/** @private */330this.applyShadow=function(ctx,shadow){331ctx.shadowColor=shadow.color;332ctx.shadowOffsetX=shadow.offsetX;333ctx.shadowOffsetY=shadow.offsetY;334ctx.shadowBlur=shadow.blur;335}336337/** @private */338this._testHit=function(ctx){339try{340varhit=ctx.getImageData(0,0,1,1).data[3]>1;341}catch(e){342if(!DisplayObject.suppressCrossDomainErrors){343throw"An error has occured. This is most likely due to security restrictions on reading canvas pixel data with local or cross-domain images.";344}345}346returnhit;347}348}