phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers.
1,825 lines (1,549 loc) • 2.18 MB
JavaScript
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2014 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*
* @overview
*
* Phaser - http://phaser.io
*
* v2.1.2 "Whitebridge" - Built: Thu Oct 09 2014 16:16:06
*
* By Richard Davey http://www.photonstorm.com @photonstorm
*
* Phaser is a fun, free and fast 2D game framework for making HTML5 games
* for desktop and mobile web browsers, supporting Canvas and WebGL rendering.
*
* Phaser uses Pixi.js for rendering, created by Mat Groves http://matgroves.com @Doormat23
* Phaser uses p2.js for full-body physics, created by Stefan Hedman https://github.com/schteppe/p2.js @schteppe
* Phaser contains a port of N+ Physics, converted by Richard Davey, original by http://www.metanetsoftware.com
*
* Many thanks to Adam Saltsman (@ADAMATOMIC) for releasing Flixel, from which both Phaser
* and my love of framework development originate.
*
* Follow development at http://phaser.io and on our forum
*
* "If you want your children to be intelligent, read them fairy tales."
* "If you want them to be more intelligent, read them more fairy tales."
* -- Albert Einstein
*/
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
(function(){
var root = this;
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
/**
* @module PIXI
*/
var PIXI = PIXI || {};
/*
*
* This file contains a lot of pixi consts which are used across the rendering engine
* @class Consts
*/
PIXI.WEBGL_RENDERER = 0;
PIXI.CANVAS_RENDERER = 1;
// useful for testing against if your lib is using pixi.
PIXI.VERSION = "v1.6.1";
// the various blend modes supported by pixi
PIXI.blendModes = {
NORMAL:0,
ADD:1,
MULTIPLY:2,
SCREEN:3,
OVERLAY:4,
DARKEN:5,
LIGHTEN:6,
COLOR_DODGE:7,
COLOR_BURN:8,
HARD_LIGHT:9,
SOFT_LIGHT:10,
DIFFERENCE:11,
EXCLUSION:12,
HUE:13,
SATURATION:14,
COLOR:15,
LUMINOSITY:16
};
// the scale modes
PIXI.scaleModes = {
DEFAULT:0,
LINEAR:0,
NEAREST:1
};
// used to create uids for various pixi objects..
PIXI._UID = 0;
if(typeof(Float32Array) != 'undefined')
{
PIXI.Float32Array = Float32Array;
PIXI.Uint16Array = Uint16Array;
}
else
{
PIXI.Float32Array = Array;
PIXI.Uint16Array = Array;
}
// interaction frequency
PIXI.INTERACTION_FREQUENCY = 30;
PIXI.AUTO_PREVENT_DEFAULT = true;
PIXI.RAD_TO_DEG = 180 / Math.PI;
PIXI.DEG_TO_RAD = Math.PI / 180;
PIXI.dontSayHello = false;
PIXI.sayHello = function (type)
{
if(PIXI.dontSayHello)return;
if ( navigator.userAgent.toLowerCase().indexOf('chrome') > -1 )
{
var args = [
'%c %c %c Pixi.js ' + PIXI.VERSION + ' - ' + type + ' %c ' + ' %c ' + ' http://www.pixijs.com/ %c %c ♥%c♥%c♥ ',
'background: #ff66a5',
'background: #ff66a5',
'color: #ff66a5; background: #030307;',
'background: #ff66a5',
'background: #ffc3dc',
'background: #ff66a5',
'color: #ff2424; background: #fff',
'color: #ff2424; background: #fff',
'color: #ff2424; background: #fff'
];
console.log.apply(console, args);
}
else if (window['console'])
{
console.log('Pixi.js ' + PIXI.VERSION + ' - http://www.pixijs.com/');
}
PIXI.dontSayHello = true;
};
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
/**
* The Matrix class is now an object, which makes it a lot faster,
* here is a representation of it :
* | a | b | tx|
* | c | d | ty|
* | 0 | 0 | 1 |
*
* @class Matrix
* @constructor
*/
PIXI.Matrix = function()
{
this.a = 1;
this.b = 0;
this.c = 0;
this.d = 1;
this.tx = 0;
this.ty = 0;
};
/**
* Creates a pixi matrix object based on the array given as a parameter
*
* @method fromArray
* @param array {Array} The array that the matrix will be filled with
*/
PIXI.Matrix.prototype.fromArray = function(array)
{
this.a = array[0];
this.b = array[1];
this.c = array[3];
this.d = array[4];
this.tx = array[2];
this.ty = array[5];
};
/**
* Creates an array from the current Matrix object
*
* @method toArray
* @param transpose {Boolean} Whether we need to transpose the matrix or not
* @return {Array} the newly created array which contains the matrix
*/
PIXI.Matrix.prototype.toArray = function(transpose)
{
if(!this.array) this.array = new Float32Array(9);
var array = this.array;
if(transpose)
{
array[0] = this.a;
array[1] = this.c;
array[2] = 0;
array[3] = this.b;
array[4] = this.d;
array[5] = 0;
array[6] = this.tx;
array[7] = this.ty;
array[8] = 1;
}
else
{
array[0] = this.a;
array[1] = this.b;
array[2] = this.tx;
array[3] = this.c;
array[4] = this.d;
array[5] = this.ty;
array[6] = 0;
array[7] = 0;
array[8] = 1;
}
return array;
};
/**
* Get a new position with the current transormation applied.
* Can be used to go from a child's coordinate space to the world coordinate space. (e.g. rendering)
*
* @method apply
* @param pos {Point} The origin
* @param [newPos] {Point} The point that the new position is assigned to (allowed to be same as input)
* @return {Point} The new point, transformed trough this matrix
*/
PIXI.Matrix.prototype.apply = function(pos, newPos)
{
newPos = newPos || new PIXI.Point();
newPos.x = this.a * pos.x + this.b * pos.y + this.tx;
newPos.y = this.c * pos.x + this.d * pos.y + this.ty;
return newPos;
};
/**
* Get a new position with the inverse of the current transormation applied.
* Can be used to go from the world coordinate space to a child's coordinate space. (e.g. input)
*
* @method apply
* @param pos {Point} The origin
* @param [newPos] {Point} The point that the new position is assigned to (allowed to be same as input)
* @return {Point} The new point, inverse-transformed trough this matrix
*/
PIXI.Matrix.prototype.applyInverse = function(pos, newPos)
{
newPos = newPos || new PIXI.Point();
var id = 1 / (this.a * this.d + this.b * -this.c);
newPos.x = this.d * id * pos.x - this.b * id * pos.y + (this.ty * this.b - this.tx * this.d) * id;
newPos.y = this.a * id * pos.y - this.c * id * pos.x + (this.tx * this.c - this.ty * this.a) * id;
return newPos;
};
PIXI.identityMatrix = new PIXI.Matrix();
PIXI.determineMatrixArrayType = function() {
return (typeof Float32Array !== 'undefined') ? Float32Array : Array;
};
/**
* The Matrix2 class will choose the best type of array to use between
* a regular javascript Array and a Float32Array if the latter is available
*
* @class Matrix2
* @constructor
*/
PIXI.Matrix2 = PIXI.determineMatrixArrayType();
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
/**
* The base class for all objects that are rendered on the screen.
* This is an abstract class and should not be used on its own rather it should be extended.
*
* @class DisplayObject
* @constructor
*/
PIXI.DisplayObject = function()
{
/**
* The coordinate of the object relative to the local coordinates of the parent.
*
* @property position
* @type Point
*/
this.position = new PIXI.Point();
/**
* The scale factor of the object.
*
* @property scale
* @type Point
*/
this.scale = new PIXI.Point(1,1);//{x:1, y:1};
/**
* The pivot point of the displayObject that it rotates around
*
* @property pivot
* @type Point
*/
this.pivot = new PIXI.Point(0,0);
/**
* The rotation of the object in radians.
*
* @property rotation
* @type Number
*/
this.rotation = 0;
/**
* The opacity of the object.
*
* @property alpha
* @type Number
*/
this.alpha = 1;
/**
* The visibility of the object.
*
* @property visible
* @type Boolean
*/
this.visible = true;
/**
* This is the defined area that will pick up mouse / touch events. It is null by default.
* Setting it is a neat way of optimising the hitTest function that the interactionManager will use (as it will not need to hit test all the children)
*
* @property hitArea
* @type Rectangle|Circle|Ellipse|Polygon
*/
this.hitArea = null;
/**
* This is used to indicate if the displayObject should display a mouse hand cursor on rollover
*
* @property buttonMode
* @type Boolean
*/
this.buttonMode = false;
/**
* Can this object be rendered
*
* @property renderable
* @type Boolean
*/
this.renderable = false;
/**
* [read-only] The display object container that contains this display object.
*
* @property parent
* @type DisplayObjectContainer
* @readOnly
*/
this.parent = null;
/**
* [read-only] The stage the display object is connected to, or undefined if it is not connected to the stage.
*
* @property stage
* @type Stage
* @readOnly
*/
this.stage = null;
/**
* [read-only] The multiplied alpha of the displayObject
*
* @property worldAlpha
* @type Number
* @readOnly
*/
this.worldAlpha = 1;
/**
* [read-only] Whether or not the object is interactive, do not toggle directly! use the `interactive` property
*
* @property _interactive
* @type Boolean
* @readOnly
* @private
*/
this._interactive = false;
/**
* This is the cursor that will be used when the mouse is over this object. To enable this the element must have interaction = true and buttonMode = true
*
* @property defaultCursor
* @type String
*
*/
this.defaultCursor = 'pointer';
/**
* [read-only] Current transform of the object based on world (parent) factors
*
* @property worldTransform
* @type Mat3
* @readOnly
* @private
*/
this.worldTransform = new PIXI.Matrix();
/**
* [NYI] Unknown
*
* @property color
* @type Array<>
* @private
*/
this.color = [];
/**
* [NYI] Holds whether or not this object is dynamic, for rendering optimization
*
* @property dynamic
* @type Boolean
* @private
*/
this.dynamic = true;
// cached sin rotation and cos rotation
this._sr = 0;
this._cr = 1;
/**
* The area the filter is applied to like the hitArea this is used as more of an optimisation
* rather than figuring out the dimensions of the displayObject each frame you can set this rectangle
*
* @property filterArea
* @type Rectangle
*/
this.filterArea = null;//new PIXI.Rectangle(0,0,1,1);
/**
* The original, cached bounds of the object
*
* @property _bounds
* @type Rectangle
* @private
*/
this._bounds = new PIXI.Rectangle(0, 0, 1, 1);
/**
* The most up-to-date bounds of the object
*
* @property _currentBounds
* @type Rectangle
* @private
*/
this._currentBounds = null;
/**
* The original, cached mask of the object
*
* @property _currentBounds
* @type Rectangle
* @private
*/
this._mask = null;
this._cacheAsBitmap = false;
this._cacheIsDirty = false;
/*
* MOUSE Callbacks
*/
/**
* A callback that is used when the users mouse rolls over the displayObject
* @method mouseover
* @param interactionData {InteractionData}
*/
/**
* A callback that is used when the users mouse leaves the displayObject
* @method mouseout
* @param interactionData {InteractionData}
*/
//Left button
/**
* A callback that is used when the users clicks on the displayObject with their mouse's left button
* @method click
* @param interactionData {InteractionData}
*/
/**
* A callback that is used when the user clicks the mouse's left button down over the sprite
* @method mousedown
* @param interactionData {InteractionData}
*/
/**
* A callback that is used when the user releases the mouse's left button that was over the displayObject
* for this callback to be fired, the mouse's left button must have been pressed down over the displayObject
* @method mouseup
* @param interactionData {InteractionData}
*/
/**
* A callback that is used when the user releases the mouse's left button that was over the displayObject but is no longer over the displayObject
* for this callback to be fired, the mouse's left button must have been pressed down over the displayObject
* @method mouseupoutside
* @param interactionData {InteractionData}
*/
//Right button
/**
* A callback that is used when the users clicks on the displayObject with their mouse's right button
* @method rightclick
* @param interactionData {InteractionData}
*/
/**
* A callback that is used when the user clicks the mouse's right button down over the sprite
* @method rightdown
* @param interactionData {InteractionData}
*/
/**
* A callback that is used when the user releases the mouse's right button that was over the displayObject
* for this callback to be fired the mouse's right button must have been pressed down over the displayObject
* @method rightup
* @param interactionData {InteractionData}
*/
/**
* A callback that is used when the user releases the mouse's right button that was over the displayObject but is no longer over the displayObject
* for this callback to be fired, the mouse's right button must have been pressed down over the displayObject
* @method rightupoutside
* @param interactionData {InteractionData}
*/
/*
* TOUCH Callbacks
*/
/**
* A callback that is used when the users taps on the sprite with their finger
* basically a touch version of click
* @method tap
* @param interactionData {InteractionData}
*/
/**
* A callback that is used when the user touches over the displayObject
* @method touchstart
* @param interactionData {InteractionData}
*/
/**
* A callback that is used when the user releases a touch over the displayObject
* @method touchend
* @param interactionData {InteractionData}
*/
/**
* A callback that is used when the user releases the touch that was over the displayObject
* for this callback to be fired, The touch must have started over the sprite
* @method touchendoutside
* @param interactionData {InteractionData}
*/
};
// constructor
PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject;
/**
* [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default
* Instead of using this function you can now simply set the interactive property to true or false
*
* @method setInteractive
* @param interactive {Boolean}
* @deprecated Simply set the `interactive` property directly
*/
PIXI.DisplayObject.prototype.setInteractive = function(interactive)
{
this.interactive = interactive;
};
/**
* Indicates if the sprite will have touch and mouse interactivity. It is false by default
*
* @property interactive
* @type Boolean
* @default false
*/
Object.defineProperty(PIXI.DisplayObject.prototype, 'interactive', {
get: function() {
return this._interactive;
},
set: function(value) {
this._interactive = value;
// TODO more to be done here..
// need to sort out a re-crawl!
if(this.stage)this.stage.dirty = true;
}
});
/**
* [read-only] Indicates if the sprite is globaly visible.
*
* @property worldVisible
* @type Boolean
*/
Object.defineProperty(PIXI.DisplayObject.prototype, 'worldVisible', {
get: function() {
var item = this;
do
{
if(!item.visible)return false;
item = item.parent;
}
while(item);
return true;
}
});
/**
* Sets a mask for the displayObject. A mask is an object that limits the visibility of an object to the shape of the mask applied to it.
* In PIXI a regular mask must be a PIXI.Graphics object. This allows for much faster masking in canvas as it utilises shape clipping.
* To remove a mask, set this property to null.
*
* @property mask
* @type Graphics
*/
Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', {
get: function() {
return this._mask;
},
set: function(value) {
if(this._mask)this._mask.isMask = false;
this._mask = value;
if(this._mask)this._mask.isMask = true;
}
});
/**
* Sets the filters for the displayObject.
* * IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer.
* To remove filters simply set this property to 'null'
* @property filters
* @type Array An array of filters
*/
Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', {
get: function() {
return this._filters;
},
set: function(value) {
if(value)
{
// now put all the passes in one place..
var passes = [];
for (var i = 0; i < value.length; i++)
{
var filterPasses = value[i].passes;
for (var j = 0; j < filterPasses.length; j++)
{
passes.push(filterPasses[j]);
}
}
// TODO change this as it is legacy
this._filterBlock = {target:this, filterPasses:passes};
}
this._filters = value;
}
});
/**
* Set weather or not a the display objects is cached as a bitmap.
* This basically takes a snap shot of the display object as it is at that moment. It can provide a performance benefit for complex static displayObjects
* To remove filters simply set this property to 'null'
* @property cacheAsBitmap
* @type Boolean
*/
Object.defineProperty(PIXI.DisplayObject.prototype, 'cacheAsBitmap', {
get: function() {
return this._cacheAsBitmap;
},
set: function(value) {
if(this._cacheAsBitmap === value)return;
if(value)
{
//this._cacheIsDirty = true;
this._generateCachedSprite();
}
else
{
this._destroyCachedSprite();
}
this._cacheAsBitmap = value;
}
});
/*
* Updates the object transform for rendering
*
* @method updateTransform
* @private
*/
PIXI.DisplayObject.prototype.updateTransform = function()
{
// TODO OPTIMIZE THIS!! with dirty
if(this.rotation !== this.rotationCache)
{
this.rotationCache = this.rotation;
this._sr = Math.sin(this.rotation);
this._cr = Math.cos(this.rotation);
}
// var localTransform = this.localTransform//.toArray();
var parentTransform = this.parent.worldTransform;//.toArray();
var worldTransform = this.worldTransform;//.toArray();
var px = this.pivot.x;
var py = this.pivot.y;
var a00 = this._cr * this.scale.x,
a01 = -this._sr * this.scale.y,
a10 = this._sr * this.scale.x,
a11 = this._cr * this.scale.y,
a02 = this.position.x - a00 * px - py * a01,
a12 = this.position.y - a11 * py - px * a10,
b00 = parentTransform.a, b01 = parentTransform.b,
b10 = parentTransform.c, b11 = parentTransform.d;
worldTransform.a = b00 * a00 + b01 * a10;
worldTransform.b = b00 * a01 + b01 * a11;
worldTransform.tx = b00 * a02 + b01 * a12 + parentTransform.tx;
worldTransform.c = b10 * a00 + b11 * a10;
worldTransform.d = b10 * a01 + b11 * a11;
worldTransform.ty = b10 * a02 + b11 * a12 + parentTransform.ty;
this.worldAlpha = this.alpha * this.parent.worldAlpha;
};
/**
* Retrieves the bounds of the displayObject as a rectangle object
*
* @method getBounds
* @return {Rectangle} the rectangular bounding area
*/
PIXI.DisplayObject.prototype.getBounds = function( matrix )
{
matrix = matrix;//just to get passed js hinting (and preserve inheritance)
return PIXI.EmptyRectangle;
};
/**
* Retrieves the local bounds of the displayObject as a rectangle object
*
* @method getLocalBounds
* @return {Rectangle} the rectangular bounding area
*/
PIXI.DisplayObject.prototype.getLocalBounds = function()
{
return this.getBounds(PIXI.identityMatrix);///PIXI.EmptyRectangle();
};
/**
* Sets the object's stage reference, the stage this object is connected to
*
* @method setStageReference
* @param stage {Stage} the stage that the object will have as its current stage reference
*/
PIXI.DisplayObject.prototype.setStageReference = function(stage)
{
this.stage = stage;
if(this._interactive)this.stage.dirty = true;
};
PIXI.DisplayObject.prototype.generateTexture = function(renderer)
{
var bounds = this.getLocalBounds();
var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0, renderer);
renderTexture.render(this, new PIXI.Point(-bounds.x, -bounds.y) );
return renderTexture;
};
PIXI.DisplayObject.prototype.updateCache = function()
{
this._generateCachedSprite();
};
/**
* Calculates the global position of the display object
*
* @method toGlobal
* @param position {Point} The world origin to calculate from
* @return {Point} A point object representing the position of this object
*/
PIXI.DisplayObject.prototype.toGlobal = function(pos)
{
this.updateTransform();
return this.worldTransform.apply(pos);
};
/**
* Calculates the local position of the display object relative to another point
*
* @method toGlobal
* @param position {Point} The world origin to calculate from
* @param [from] {DisplayObject} The DisplayObject to calculate the global position from
* @return {Point} A point object representing the position of this object
*/
PIXI.DisplayObject.prototype.toLocal = function(pos, from)
{
if (from)
{
pos = from.toGlobal(pos);
}
this.updateTransform();
return this.worldTransform.applyInverse(pos);
};
PIXI.DisplayObject.prototype._renderCachedSprite = function(renderSession)
{
this._cachedSprite.worldAlpha = this.worldAlpha;
if(renderSession.gl)
{
PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession);
}
else
{
PIXI.Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession);
}
};
PIXI.DisplayObject.prototype._generateCachedSprite = function()//renderSession)
{
this._cacheAsBitmap = false;
var bounds = this.getLocalBounds();
if(!this._cachedSprite)
{
var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0);//, renderSession.renderer);
this._cachedSprite = new PIXI.Sprite(renderTexture);
this._cachedSprite.worldTransform = this.worldTransform;
}
else
{
this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0);
}
//REMOVE filter!
var tempFilters = this._filters;
this._filters = null;
this._cachedSprite.filters = tempFilters;
this._cachedSprite.texture.render(this, new PIXI.Point(-bounds.x, -bounds.y) );
this._cachedSprite.anchor.x = -( bounds.x / bounds.width );
this._cachedSprite.anchor.y = -( bounds.y / bounds.height );
this._filters = tempFilters;
this._cacheAsBitmap = true;
};
/**
* Renders the object using the WebGL renderer
*
* @method _renderWebGL
* @param renderSession {RenderSession}
* @private
*/
PIXI.DisplayObject.prototype._destroyCachedSprite = function()
{
if(!this._cachedSprite)return;
this._cachedSprite.texture.destroy(true);
// console.log("DESTROY")
// let the gc collect the unused sprite
// TODO could be object pooled!
this._cachedSprite = null;
};
PIXI.DisplayObject.prototype._renderWebGL = function(renderSession)
{
// OVERWRITE;
// this line is just here to pass jshinting :)
renderSession = renderSession;
};
/**
* Renders the object using the Canvas renderer
*
* @method _renderCanvas
* @param renderSession {RenderSession}
* @private
*/
PIXI.DisplayObject.prototype._renderCanvas = function(renderSession)
{
// OVERWRITE;
// this line is just here to pass jshinting :)
renderSession = renderSession;
};
/**
* The position of the displayObject on the x axis relative to the local coordinates of the parent.
*
* @property x
* @type Number
*/
Object.defineProperty(PIXI.DisplayObject.prototype, 'x', {
get: function() {
return this.position.x;
},
set: function(value) {
this.position.x = value;
}
});
/**
* The position of the displayObject on the y axis relative to the local coordinates of the parent.
*
* @property y
* @type Number
*/
Object.defineProperty(PIXI.DisplayObject.prototype, 'y', {
get: function() {
return this.position.y;
},
set: function(value) {
this.position.y = value;
}
});
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
/**
* A DisplayObjectContainer represents a collection of display objects.
* It is the base class of all display objects that act as a container for other objects.
*
* @class DisplayObjectContainer
* @extends DisplayObject
* @constructor
*/
PIXI.DisplayObjectContainer = function()
{
PIXI.DisplayObject.call( this );
/**
* [read-only] The array of children of this container.
*
* @property children
* @type Array<DisplayObject>
* @readOnly
*/
this.children = [];
};
// constructor
PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype );
PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer;
/**
* The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set
*
* @property width
* @type Number
*/
Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'width', {
get: function() {
return this.scale.x * this.getLocalBounds().width;
},
set: function(value) {
var width = this.getLocalBounds().width;
if(width !== 0)
{
this.scale.x = value / ( width/this.scale.x );
}
else
{
this.scale.x = 1;
}
this._width = value;
}
});
/**
* The height of the displayObjectContainer, setting this will actually modify the scale to achieve the value set
*
* @property height
* @type Number
*/
Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'height', {
get: function() {
return this.scale.y * this.getLocalBounds().height;
},
set: function(value) {
var height = this.getLocalBounds().height;
if(height !== 0)
{
this.scale.y = value / ( height/this.scale.y );
}
else
{
this.scale.y = 1;
}
this._height = value;
}
});
/**
* Adds a child to the container.
*
* @method addChild
* @param child {DisplayObject} The DisplayObject to add to the container
*/
PIXI.DisplayObjectContainer.prototype.addChild = function(child)
{
return this.addChildAt(child, this.children.length);
};
/**
* Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown
*
* @method addChildAt
* @param child {DisplayObject} The child to add
* @param index {Number} The index to place the child in
*/
PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index)
{
if(index >= 0 && index <= this.children.length)
{
if(child.parent)
{
child.parent.removeChild(child);
}
child.parent = this;
this.children.splice(index, 0, child);
if(this.stage)child.setStageReference(this.stage);
return child;
}
else
{
throw new Error(child + ' The index '+ index +' supplied is out of bounds ' + this.children.length);
}
};
/**
* [NYI] Swaps the depth of 2 displayObjects
*
* @method swapChildren
* @param child {DisplayObject}
* @param child2 {DisplayObject}
* @private
*/
PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2)
{
if(child === child2) {
return;
}
var index1 = this.children.indexOf(child);
var index2 = this.children.indexOf(child2);
if(index1 < 0 || index2 < 0) {
throw new Error('swapChildren: Both the supplied DisplayObjects must be a child of the caller.');
}
this.children[index1] = child2;
this.children[index2] = child;
};
/**
* Returns the child at the specified index
*
* @method getChildAt
* @param index {Number} The index to get the child from
*/
PIXI.DisplayObjectContainer.prototype.getChildAt = function(index)
{
if(index >= 0 && index < this.children.length)
{
return this.children[index];
}
else
{
throw new Error('Supplied index does not exist in the child list, or the supplied DisplayObject must be a child of the caller');
}
};
/**
* Removes a child from the container.
*
* @method removeChild
* @param child {DisplayObject} The DisplayObject to remove
*/
PIXI.DisplayObjectContainer.prototype.removeChild = function(child)
{
return this.removeChildAt( this.children.indexOf( child ) );
};
/**
* Removes a child from the specified index position in the child list of the container.
*
* @method removeChildAt
* @param index {Number} The index to get the child from
*/
PIXI.DisplayObjectContainer.prototype.removeChildAt = function(index)
{
var child = this.getChildAt( index );
if(this.stage)
child.removeStageReference();
child.parent = undefined;
this.children.splice( index, 1 );
return child;
};
/**
* Removes all child instances from the child list of the container.
*
* @method removeChildren
* @param beginIndex {Number} The beginning position. Predefined value is 0.
* @param endIndex {Number} The ending position. Predefined value is children's array length.
*/
PIXI.DisplayObjectContainer.prototype.removeChildren = function(beginIndex, endIndex)
{
var begin = beginIndex || 0;
var end = typeof endIndex === 'number' ? endIndex : this.children.length;
var range = end - begin;
if (range > 0 && range <= end)
{
var removed = this.children.splice(begin, range);
for (var i = 0; i < removed.length; i++) {
var child = removed[i];
if(this.stage)
child.removeStageReference();
child.parent = undefined;
}
return removed;
}
else if (range === 0 && this.children.length === 0)
{
return [];
}
else
{
throw new Error( 'Range Error, numeric values are outside the acceptable range' );
}
};
/*
* Updates the container's childrens transform for rendering
*
* @method updateTransform
* @private
*/
PIXI.DisplayObjectContainer.prototype.updateTransform = function()
{
//this._currentBounds = null;
if(!this.visible)return;
PIXI.DisplayObject.prototype.updateTransform.call( this );
if(this._cacheAsBitmap)return;
for(var i=0,j=this.children.length; i<j; i++)
{
this.children[i].updateTransform();
}
};
/**
* Retrieves the bounds of the displayObjectContainer as a rectangle object
*
* @method getBounds
* @return {Rectangle} the rectangular bounding area
*/
PIXI.DisplayObjectContainer.prototype.getBounds = function(matrix)
{
if(this.children.length === 0)return PIXI.EmptyRectangle;
// TODO the bounds have already been calculated this render session so return what we have
if(matrix)
{
var matrixCache = this.worldTransform;
this.worldTransform = matrix;
this.updateTransform();
this.worldTransform = matrixCache;
}
var minX = Infinity;
var minY = Infinity;
var maxX = -Infinity;
var maxY = -Infinity;
var childBounds;
var childMaxX;
var childMaxY;
var childVisible = false;
for(var i=0,j=this.children.length; i<j; i++)
{
var child = this.children[i];
if(!child.visible)continue;
childVisible = true;
childBounds = this.children[i].getBounds( matrix );
minX = minX < childBounds.x ? minX : childBounds.x;
minY = minY < childBounds.y ? minY : childBounds.y;
childMaxX = childBounds.width + childBounds.x;
childMaxY = childBounds.height + childBounds.y;
maxX = maxX > childMaxX ? maxX : childMaxX;
maxY = maxY > childMaxY ? maxY : childMaxY;
}
if(!childVisible)
return PIXI.EmptyRectangle;
var bounds = this._bounds;
bounds.x = minX;
bounds.y = minY;
bounds.width = maxX - minX;
bounds.height = maxY - minY;
// TODO: store a reference so that if this function gets called again in the render cycle we do not have to recalculate
//this._currentBounds = bounds;
return bounds;
};
PIXI.DisplayObjectContainer.prototype.getLocalBounds = function()
{
var matrixCache = this.worldTransform;
this.worldTransform = PIXI.identityMatrix;
for(var i=0,j=this.children.length; i<j; i++)
{
this.children[i].updateTransform();
}
var bounds = this.getBounds();
this.worldTransform = matrixCache;
return bounds;
};
/**
* Sets the container's stage reference, the stage this object is connected to
*
* @method setStageReference
* @param stage {Stage} the stage that the container will have as its current stage reference
*/
PIXI.DisplayObjectContainer.prototype.setStageReference = function(stage)
{
this.stage = stage;
if(this._interactive)this.stage.dirty = true;
for(var i=0,j=this.children.length; i<j; i++)
{
var child = this.children[i];
child.setStageReference(stage);
}
};
/**
* removes the current stage reference of the container
*
* @method removeStageReference
*/
PIXI.DisplayObjectContainer.prototype.removeStageReference = function()
{
for(var i=0,j=this.children.length; i<j; i++)
{
var child = this.children[i];
child.removeStageReference();
}
if(this._interactive)this.stage.dirty = true;
this.stage = null;
};
/**
* Renders the object using the WebGL renderer
*
* @method _renderWebGL
* @param renderSession {RenderSession}
* @private
*/
PIXI.DisplayObjectContainer.prototype._renderWebGL = function(renderSession)
{
if(!this.visible || this.alpha <= 0)return;
if(this._cacheAsBitmap)
{
this._renderCachedSprite(renderSession);
return;
}
var i,j;
if(this._mask || this._filters)
{
// push filter first as we need to ensure the stencil buffer is correct for any masking
if(this._filters)
{
renderSession.spriteBatch.flush();
renderSession.filterManager.pushFilter(this._filterBlock);
}
if(this._mask)
{
renderSession.spriteBatch.stop();
renderSession.maskManager.pushMask(this.mask, renderSession);
renderSession.spriteBatch.start();
}
// simple render children!
for(i=0,j=this.children.length; i<j; i++)
{
this.children[i]._renderWebGL(renderSession);
}
renderSession.spriteBatch.stop();
if(this._mask)renderSession.maskManager.popMask(this._mask, renderSession);
if(this._filters)renderSession.filterManager.popFilter();
renderSession.spriteBatch.start();
}
else
{
// simple render children!
for(i=0,j=this.children.length; i<j; i++)
{
this.children[i]._renderWebGL(renderSession);
}
}
};
/**
* Renders the object using the Canvas renderer
*
* @method _renderCanvas
* @param renderSession {RenderSession}
* @private
*/
PIXI.DisplayObjectContainer.prototype._renderCanvas = function(renderSession)
{
if(this.visible === false || this.alpha === 0)return;
if(this._cacheAsBitmap)
{
this._renderCachedSprite(renderSession);
return;
}
if(this._mask)
{
renderSession.maskManager.pushMask(this._mask, renderSession.context);
}
for(var i=0,j=this.children.length; i<j; i++)
{
var child = this.children[i];
child._renderCanvas(renderSession);
}
if(this._mask)
{
renderSession.maskManager.popMask(renderSession.context);
}
};
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
/**
* The Sprite object is the base for all textured objects that are rendered to the screen
*
* @class Sprite
* @extends DisplayObjectContainer
* @constructor
* @param texture {Texture} The texture for this sprite
*
* A sprite can be created directly from an image like this :
* var sprite = new PIXI.Sprite.fromImage('assets/image.png');
* yourStage.addChild(sprite);
* then obviously don't forget to add it to the stage you have already created
*/
PIXI.Sprite = function(texture)
{
PIXI.DisplayObjectContainer.call( this );
/**
* The anchor sets the origin point of the texture.
* The default is 0,0 this means the texture's origin is the top left
* Setting than anchor to 0.5,0.5 means the textures origin is centred
* Setting the anchor to 1,1 would mean the textures origin points will be the bottom right corner
*
* @property anchor
* @type Point
*/
this.anchor = new PIXI.Point();
/**
* The texture that the sprite is using
*
* @property texture
* @type Texture
*/
this.texture = texture;
/**
* The width of the sprite (this is initially set by the texture)
*
* @property _width
* @type Number
* @private
*/
this._width = 0;
/**
* The height of the sprite (this is initially set by the texture)
*
* @property _height
* @type Number
* @private
*/
this._height = 0;
/**
* The tint applied to the sprite. This is a hex value
*
* @property tint
* @type Number
* @default 0xFFFFFF
*/
this.tint = 0xFFFFFF;// * Math.random();
/**
* The blend mode to be applied to the sprite
*
* @property blendMode
* @type Number
* @default PIXI.blendModes.NORMAL;
*/
this.blendMode = PIXI.blendModes.NORMAL;
if(texture.baseTexture.hasLoaded)
{
this.onTextureUpdate();
}
else
{
this.onTextureUpdateBind = this.onTextureUpdate.bind(this);
this.texture.addEventListener( 'update', this.onTextureUpdateBind );
}
this.renderable = true;
};
// constructor
PIXI.Sprite.prototype = Object.create( PIXI.DisplayObjectContainer.prototype );
PIXI.Sprite.prototype.constructor = PIXI.Sprite;
/**
* The width of the sprite, setting this will actually modify the scale to achieve the value set
*
* @property width
* @type Number
*/
Object.defineProperty(PIXI.Sprite.prototype, 'width', {
get: function() {
return this.scale.x * this.texture.frame.width;
},
set: function(value) {
this.scale.x = value / this.texture.frame.width;
this._width = value;
}
});
/**
* The height of the sprite, setting this will actually modify the scale to achieve the value set
*
* @property height
* @type Number
*/
Object.defineProperty(PIXI.Sprite.prototype, 'height', {
get: function() {
return this.scale.y * this.texture.frame.height;
},
set: function(value) {
this.scale.y = value / this.texture.frame.height;
this._height = value;
}
});
/**
* Sets the texture of the sprite
*
* @method setTexture
* @param texture {Texture} The PIXI texture that is displayed by the sprite
*/
PIXI.Sprite.prototype.setTexture = function(texture)
{
this.texture = texture;
this.cachedTint = 0xFFFFFF;
};
/**
* When the texture is updated, this event will fire to update the scale and frame
*
* @method onTextureUpdate
* @param event
* @private
*/
PIXI.Sprite.prototype.onTextureUpdate = function()
{
// so if _width is 0 then width was not set..
if(this._width)this.scale.x = this._width / this.texture.frame.width;
if(this._height)this.scale.y = this._height / this.texture.frame.height;
//this.updateFrame = true;
};
/**
* Returns the framing rectangle of the sprite as a PIXI.Rectangle object
*
* @method getBounds
* @param matrix {Matrix} the transformation matrix of the sprite
* @return {Rectangle} the framing rectangle
*/
PIXI.Sprite.prototype.getBounds = function(matrix)
{
var width = this.texture.frame.width;
var height = this.texture.frame.height;
var w0 = width * (1-this.anchor.x);
var w1 = width * -this.anchor.x;
var h0 = height * (1-this.anchor.y);
var h1 = height * -this.anchor.y;
var worldTransform = matrix || this.worldTransform ;
var a = worldTransform.a;
var b = worldTransform.c;
var c = worldTransform.b;
var d = worldTransform.d;
var tx = worldTransform.tx;
var ty = worldTransform.ty;
var x1 = a * w1 + c * h1 + tx;
var y1 = d * h1 + b * w1 + ty;
var x2 = a * w0 + c * h1 + tx;
var y2 = d * h1 + b * w0 + ty;
var x3 = a * w0 + c * h0 + tx;
var y3 = d * h0 + b * w0 + ty;
var x4 = a * w1 + c * h0 + tx;
var y4 = d * h0 + b * w1 + ty;
var maxX = -Infinity;
var maxY = -Infinity;
var minX = Infinity;
var minY = Infinity;
minX = x1 < minX ? x1 : minX;
minX = x2 < minX ? x2 : minX;
minX = x3 < minX ? x3 : minX;
minX = x4 < minX ? x4 : minX;
minY = y1 < minY ? y1 : minY;
minY = y2 < minY ? y2 : minY;
minY = y3 < minY ? y3 : minY;
minY = y4 < minY ? y4 : minY;
maxX = x1 > maxX ? x1 : maxX;
maxX = x2 > maxX ? x2 : maxX;
maxX = x3 > maxX ? x3 : maxX;
maxX = x4 > maxX ? x4 : maxX;
maxY = y1 > maxY ? y1 : maxY;
maxY = y2 > maxY ? y2 : maxY;
maxY = y3 > maxY ? y3 : maxY;
maxY = y4 > maxY ? y4 : maxY;
var bounds = this._bounds;
bounds.x = minX;
bounds.width = maxX - minX;
bounds.y = minY;
bounds.height = maxY - minY;
// store a reference so that if this function gets called again in the render cycle we do not have to recalculate
this._currentBounds = bounds;
return bounds;
};
/**
* Renders the object using the WebGL renderer
*
* @method _renderWebGL
* @param renderSession {RenderSession}
* @private
*/
PIXI.Sprite.prototype._renderWebGL = function(renderSession)
{
// if the sprite is not visible or the alpha is 0 then no need to render this element
if(!this.visible || this.alpha <= 0)return;
var i,j;
// do a quick check to see if this element has a mask or a filter.
if(this._mask || this._filters)
{
var spriteBatch = renderSession.spriteBatch;
// push filter first as we need to ensure the stencil buffer is correct for any masking
if(this._filters)
{
spriteBatch.flush();
renderSession.filterManager.pushFilter(this._filterBlock);
}
if(this._mask)
{
spriteBatch.stop();
renderSession.maskManager.pushMask(this.mask, renderSession);
spriteBatch.start();
}
// add this sprite to the batch
spriteBatch.render(this);
// now loop through the children and make sure they get rendered
for(i=0,j=this.children.length; i<j; i++)
{
this.children[i]._renderWebGL(renderSession);
}
// time to stop the sprite batch as either a mask element or a filter draw will happen next
spriteBatch.stop();
if(this._mask)renderSession.maskManager.popMask(this._mask, renderSession);
if(this._filters)renderSession.filterManager.popFilter();
spriteBatch.start();
}
else
{
renderSession.spriteBatch.render(this);
// simple render children!
for(i=0,j=this.children.length; i<j; i++)
{
this.children[i]._renderWebGL(renderSession);
}
}
//TODO check culling
};
/**
* Renders the object using the Canvas renderer
*
* @method _renderCanvas
* @param renderSession {RenderSession}
* @private
*/
PIXI.Sprite.prototype._renderCanvas = function(renderSession)
{
// If the sprite is not visible or the alpha is 0 then no need to render this element
if (this.visible === false || this.alpha === 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) return;
if (this.blendMode !== renderSession.currentBlendMode)
{
renderSession.currentBlendMode = this.blendMode;
renderSession.context.globalCompositeOperation = PIXI.blendModesCanvas[renderSession.currentBlendMode];
}
if (this._mask)
{
renderSession.maskManager.pushMask(this._mask, renderSession.context);
}
// Ignore null sources
if (this.texture.valid)
{
renderSession.context.globalAlpha = this.worldAlpha;
// Allow for pixel rounding
if (renderSession.roundPixels)
{
renderSession.context.setTransform(
this.worldTransform.a,
this.worldTransform.c,
this.worldTransform.b,
this.worldTransform.d,
this.worldTransform.tx | 0,
this.worldTransform.ty | 0);
}
else
{
renderSession.context.setTransform(
this.worldTransform.a,
this.worldTransform.c,
this.worldTransform.b,
this.worldTransform.d,
this.worldTransform.tx,
this.worldTransform.ty);
}
// If smoothingEnabled is supported and we need to change the smoothing property for this texture
if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode)
{
renderSession.scaleMode = this.texture.baseTexture.scaleMode;
renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === PIXI.scaleModes.LINEAR);
}
// If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions
var dx = (this.texture.trim) ? this.texture.trim.x - this.anchor.x * this.texture.trim.width : this.anchor.x * -this.texture.frame.width;
var dy = (this.texture.trim) ? this.texture.trim.y - this.anchor.y * this.texture.trim.height : this.anchor.y * -this.texture.frame.height;
if (this.tint !== 0xFFFFFF)
{
if (this.cachedTint !== this.tint)
{
this.cachedTint = this.tint;
// TODO clean up caching - how to clean up the caches?
this.tintedTexture = PIXI.CanvasTinter.getTintedTexture(this, this.tint);
}
renderSession.context.drawImage(
this.tintedTexture,
0,
0,
this.texture.crop.width,
this.texture.crop.height,
dx,
dy,
this.texture.crop.width,
this.texture.crop.height);
}
else
{
renderSession.context.drawImage(
this.texture.baseTexture.source,
this.texture.crop.x,
this.texture.crop.y,
this.textu