Merge pull request #1344 from pnstickne/wip-dom-viewport

ScaleManager + DOM
This commit is contained in:
Richard Davey 2014-11-18 09:22:04 +00:00
commit 53422b97f4
4 changed files with 444 additions and 273 deletions

View file

@ -11,24 +11,27 @@
*
* The Game size is the logical size of the game; the Display canvas has size as an HTML element.
*
* The calculations of these are heavily influenced by the Parent size which is the computed
* The calculations of these are heavily influenced by the bounding Parent size which is the computed
* dimenstions of the Display canvas's Parent container/element - the _effective CSS rules of the
* canvas's Parent element play an important role_ in the operation of the ScaleManager.
*
* The Display canvas - or Game size, depending {@link Phaser.ScaleManager#scaleMode} - is updated to best utilize the Parent size.
* The Display canvas - or Game size, depending {@link Phaser.ScaleManager#scaleMode scaleMode} - is updated to best utilize the Parent size.
* When in Fullscreen mode or with `parentIsWindow` the Parent size is that of the visual viewport (see {@link Phaser.ScaleManager#getParentBounds getParentBounds}).
*
* Parent and Display canvas containment guidelines:
*
* - Style the Parent element (of the game canvas) to control the Parent size and
* thus the Display canvas's size and layout.
* The Parent element's CSS styles should _effectivelly_ apply maximum (and minimum) bounding behavior.
*
* - The Display canvas layout CSS styles (ie. margins, size) should generally not be altered manually as
* - The Parent element's CSS styles should _effectively_ apply maximum (and minimum) bounding behavior.
*
* - The Parent element should _not_ apply a padding as this is not accounted for.
* If a padding is required apply it to the Parent's parent or apply a margin to the Parent.
*
* - The Display canvas layout CSS styles (ie. margins, size) should not be altered/specified as
* they may be updated by the ScaleManager.
*
* - When running in Fullscreen mode or as `parentIsWindow` the Parent size is that of the viewport.
*
* See {@link Phaser.ScaleManager#getParentBounds}.
* ---
*
* Some parts of ScaleManager were inspired by the research of Ryan Van Etten, released under MIT License 2013.
*
@ -52,6 +55,14 @@ Phaser.ScaleManager = function (game, width, height) {
*/
this.game = game;
/**
* Provides access to some cross-device DOM functions.
* @property {Phaser.DOM} dom
* @protected
* @readonly
*/
this.dom = Phaser.DOM;
/**
* _EXPERIMENTAL:_ A responsive grid on which you can align game objects.
* @property {Phaser.FlexGrid} grid
@ -78,6 +89,7 @@ Phaser.ScaleManager = function (game, width, height) {
* Change with `setMinMax`.
* @property {number} minWidth
* @readonly
* @protected
*/
this.minWidth = null;
@ -87,6 +99,7 @@ Phaser.ScaleManager = function (game, width, height) {
* Change with `setMinMax`.
* @property {number} maxWidth
* @readonly
* @protected
*/
this.maxWidth = null;
@ -95,6 +108,7 @@ Phaser.ScaleManager = function (game, width, height) {
* Change with `setMinMax`.
* @property {number} minHeight
* @readonly
* @protected
*/
this.minHeight = null;
@ -104,6 +118,7 @@ Phaser.ScaleManager = function (game, width, height) {
* Change with `setMinMax`.
* @property {number} maxHeight
* @readonly
* @protected
*/
this.maxHeight = null;
@ -138,6 +153,9 @@ Phaser.ScaleManager = function (game, width, height) {
/**
* True if the `forceLandscape` or `forcePortrait` are set and do not agree with the browser orientation.
*
* This value is not updated immediately.
*
* @property {boolean} incorrectOrientation
* @readonly
* @protected
@ -162,11 +180,37 @@ Phaser.ScaleManager = function (game, width, height) {
* The maximum number of times a canvas will be resized (in a row) in order to fill the browser.
* @property {number} maxIterations
* @protected
* @see {@link Phaser.ScaleManger#refresh}
* @see {@link Phaser.ScaleManger#refresh refresh}
* @deprecated 2.2.0 - This is not used anymore as reflow iterations are "automatic".
*/
this.maxIterations = 5;
/**
* This signal is dispatched when the orientation changes _or_ the validity of the current orientation changes.
*
* The signal is supplied with the following arguments:
* - `scale` - the ScaleManager object
* - `prevOrientation`, a string - The previous orientation as per {@link Phaser.ScaleManager#screenOrientation screenOrientation}.
* - `wasIncorrect`, a boolean - True if the previous orientation was last determined to be incorrect.
*
* Access the current orientation and validity with `scale.screenOrientation` and `scale.incorrectOrientation`.
* Thus the following tests can be done:
*
* // The orientation itself changed:
* scale.screenOrientation !== prevOrientation
* // The orientation just became incorrect:
* scale.incorrectOrientation && !wasIncorrect
*
* It is possible that this signal is triggered after `forceOrientation` so the orientation
* correctness changes even if the orientation itself does not change.
*
* This is signaled from `preUpdate` (or `pauseUpdate`) _even when_ the game is paused.
*
* @property {Phaser.Signal} onOrientationChange
* @public
*/
this.onOrientationChange = new Phaser.Signal();
/**
* This signal is dispatched when the browser enters landscape orientation, having been in portrait.
*
@ -174,6 +218,7 @@ Phaser.ScaleManager = function (game, width, height) {
*
* @property {Phaser.Signal} enterLandscape
* @public
* @deprecated 2.2.0 - Use {@link Phaser.ScaleManager#onOrientationChange onOrientationChange}
*/
this.enterLandscape = new Phaser.Signal();
@ -184,6 +229,7 @@ Phaser.ScaleManager = function (game, width, height) {
*
* @property {Phaser.Signal} enterPortrait
* @public
* @deprecated 2.2.0 - Use {@link Phaser.ScaleManager#onOrientationChange onOrientationChange}
*/
this.enterPortrait = new Phaser.Signal();
@ -194,6 +240,7 @@ Phaser.ScaleManager = function (game, width, height) {
*
* @property {Phaser.Signal} enterIncorrectOrientation
* @public
* @deprecated 2.2.0 - Use {@link Phaser.ScaleManager#onOrientationChange onOrientationChange}
*/
this.enterIncorrectOrientation = new Phaser.Signal();
@ -202,8 +249,9 @@ Phaser.ScaleManager = function (game, width, height) {
*
* This is signaled from `preUpdate` (or `pauseUpdate`) _even when_ the game is paused.
*
* @property {Phaser.Signal} leaveIncorrectOrientation -
* @property {Phaser.Signal} leaveIncorrectOrientation
* @public
* @deprecated 2.2.0 - Use {@link Phaser.ScaleManager#onOrientationChange onOrientationChange}
*/
this.leaveIncorrectOrientation = new Phaser.Signal();
@ -219,7 +267,7 @@ Phaser.ScaleManager = function (game, width, height) {
*
* @property {?DOMElement} fullScreenTarget
* @default
* @deprecated 2.2.0 - See {@link Phaser.ScaleManger#onFulLScreenInit} and {@link Phaser.ScaleManager#createFullScreenTarget}.
* @deprecated 2.2.0 - See {@link Phaser.ScaleManger#onFulLScreenInit onFulLScreenInit} and {@link Phaser.ScaleManager#createFullScreenTarget createFullScreenTarget}.
*/
this.fullScreenTarget = null;
@ -235,10 +283,10 @@ Phaser.ScaleManager = function (game, width, height) {
* This signal is dispatched when fullscreen mode is ready to be initialized but
* before the fullscreen request.
*
* The signal is passed a single argument, an object in the form `{targetElement: DOMElement}`.
* The signal is passed two arguments: `scale` (the ScaleManager), and an object in the form `{targetElement: DOMElement}`.
*
* The `targetElement` is the {@link Phaser.ScaleManager#fullScreenTarget} element,
* if such is assigned, or a new element created by {@link Phaser.ScaleManager#createFullScreenTarget}.
* The `targetElement` is the {@link Phaser.ScaleManager#fullScreenTarget fullScreenTarget} element,
* if such is assigned, or a new element created by {@link Phaser.ScaleManager#createFullScreenTarget createFullScreenTarget}.
*
* Custom CSS styling or resets can be applied to `targetElement` as required.
*
@ -247,18 +295,41 @@ Phaser.ScaleManager = function (game, width, height) {
* the duration of the fullscreen mode, and restored to it's original DOM location when fullscreen is exited.
* - The `targetElement` is moved/reparanted within the DOM and may have its CSS styles updated.
*
* The behavior of a pre-assigned target element is covered in {@link Phaser.ScaleManager#fullScreenTarget}.
* The behavior of a pre-assigned target element is covered in {@link Phaser.ScaleManager#fullScreenTarget fullScreenTarget}.
*
* @property {Phaser.Signal} onFullScreenInit
* @public
*/
this.onFullScreenInit = new Phaser.Signal();
/**
* This signal is dispatched when the browser enters or leaves fullscreen mode, if supported.
*
* The signal is supplied with a single argument: `scale` (the ScaleManager). Use `scale.isFullScreen` to determine
* if currently running in Fullscreen mode.
*
* @property {Phaser.Signal} onFullScreenChange
* @public
*/
this.onFullScreenChange = new Phaser.Signal();
/**
* This signal is dispatched when the browser fails to enter fullscreen mode;
* or if the device does not support fullscreen mode and `startFullScreen` is invoked.
*
* The signal is supplied with a single argument: `scale` (the ScaleManager).
*
* @property {Phaser.Signal} onFullScreenError
* @public
*/
this.onFullScreenError = new Phaser.Signal();
/**
* This signal is dispatched when the browser enters fullscreen mode, if supported.
*
* @property {Phaser.Signal} enterFullScreen
* @public
* @deprecated 2.2.0 - Use {@link Phaser.ScaleManager#onFullScreenChange onFullScreenChange}
*/
this.enterFullScreen = new Phaser.Signal();
@ -267,6 +338,7 @@ Phaser.ScaleManager = function (game, width, height) {
*
* @property {Phaser.Signal} leaveFullScreen
* @public
* @deprecated 2.2.0 - Use {@link Phaser.ScaleManager#onFullScreenChange onFullScreenChange}
*/
this.leaveFullScreen = new Phaser.Signal();
@ -276,8 +348,9 @@ Phaser.ScaleManager = function (game, width, height) {
*
* @property {Phaser.Signal} fullScreenFailed
* @public
* @deprecated 2.2.0 - Use {@link Phaser.ScaleManager#onFullScreenError onFullScreenError}
*/
this.fullScreenFailed = new Phaser.Signal();
this.fullScreenFailed = this.onFullScreenError;
/**
* The _last known_ orientation of the screen, as defined in the Window Screen Web API.
@ -287,7 +360,7 @@ Phaser.ScaleManager = function (game, width, height) {
* @readonly
* @public
*/
this.screenOrientation = Phaser.DOM.getScreenOrientation();
this.screenOrientation = this.dom.getScreenOrientation();
/**
* The _current_ scale factor based on the game dimensions vs. the scaled dimensions.
@ -306,8 +379,10 @@ Phaser.ScaleManager = function (game, width, height) {
/**
* The Display canvas is aligned by adjusting the margins; the last margins are stored here.
*
* @property {Bounds-like} margin
* @readonly
* @protected
*/
this.margin = {left: 0, top: 0, right: 0, bottom: 0, x: 0, y: 0};
@ -341,7 +416,12 @@ Phaser.ScaleManager = function (game, width, height) {
this.event = null;
/**
* The edges on which to constrain the Canvas _to_ the Window viewport in _addition_ to any restrictions of the parent container.
* The edges on which to constrain the Display canvas _to_ the viewport in _addition_ to any restrictions of the Parent element.
*
* The viewport does not include the scrollbars by default.
*
* Call {@link Phaser.ScaleManager#refresh refresh} after modifying this object.
*
* @property {boolean} windowConstraints
* @default
*/
@ -365,7 +445,7 @@ Phaser.ScaleManager = function (game, width, height) {
*
* @property {boolean} [forceMinimumDocumentHeight=true] - If enabled the document element's minimum height is explicity set on updates.
*
* @property {boolean} [showAllCanExpand=true] - If enabled then SHOW_ALL is allowed to try and expand it's non-window parent. It may be necessary for the parent element to impose CSS width/height restrictions.
* @property {boolean} [canExpandParent=true] - If enabled then SHOW_ALL and USER_SCALE modes can try and expand the parent element. It may be necessary for the parent element to impose CSS width/height restrictions.
*/
this.compatibility = {
supportsFullScreen: false,
@ -373,7 +453,7 @@ Phaser.ScaleManager = function (game, width, height) {
noMargins: false,
scrollTo: null,
forceMinimumDocumentHeight: true,
showAllCanExpand: true
canExpandParent: true
};
/**
@ -420,7 +500,7 @@ Phaser.ScaleManager = function (game, width, height) {
* @property {integer} trackParentInterval
* @default
* @protected
* @see {@link Phaser.ScaleManager#refresh}
* @see {@link Phaser.ScaleManager#refresh refresh}
*/
this.trackParentInterval = 2000;
@ -476,6 +556,13 @@ Phaser.ScaleManager = function (game, width, height) {
*/
this._userScaleFactor = new Phaser.Point(1, 1);
/**
* The user-supplied scale trim, used with the USER_SCALE scaling mode.
* @property {Phaser.Point} _userScaleTrim
* @private
*/
this._userScaleTrim = new Phaser.Point(0, 0);
/**
* The last time the bounds were checked in `preUpdate`.
* @property {number} _lastUpdate
@ -530,7 +617,7 @@ Phaser.ScaleManager = function (game, width, height) {
};
/**
* A scale mode that stretches content to fill all available space - see {@link Phaser.ScaleManager#scaleMode}.
* A scale mode that stretches content to fill all available space - see {@link Phaser.ScaleManager#scaleMode scaleMode}.
*
* @constant
* @type {integer}
@ -538,7 +625,7 @@ Phaser.ScaleManager = function (game, width, height) {
Phaser.ScaleManager.EXACT_FIT = 0;
/**
* A scale mode that prevents any scaling - see {@link Phaser.ScaleManager#scaleMode}.
* A scale mode that prevents any scaling - see {@link Phaser.ScaleManager#scaleMode scaleMode}.
*
* @constant
* @type {integer}
@ -546,7 +633,7 @@ Phaser.ScaleManager.EXACT_FIT = 0;
Phaser.ScaleManager.NO_SCALE = 1;
/**
* A scale mode that shows the entire game while maintaining proportions - see {@link Phaser.ScaleManager#scaleMode}.
* A scale mode that shows the entire game while maintaining proportions - see {@link Phaser.ScaleManager#scaleMode scaleMode}.
*
* @constant
* @type {integer}
@ -554,7 +641,7 @@ Phaser.ScaleManager.NO_SCALE = 1;
Phaser.ScaleManager.SHOW_ALL = 2;
/**
* A scale mode that causes the Game size to change - see {@link Phaser.ScaleManager#scaleMode}.
* A scale mode that causes the Game size to change - see {@link Phaser.ScaleManager#scaleMode scaleMode}.
*
* @constant
* @type {integer}
@ -562,10 +649,9 @@ Phaser.ScaleManager.SHOW_ALL = 2;
Phaser.ScaleManager.RESIZE = 3;
/**
* A scale mode that allows a custom scale factor - see {@link Phaser.ScaleManager#scaleMode}.
* A scale mode that allows a custom scale factor - see {@link Phaser.ScaleManager#scaleMode scaleMode}.
*
* @constant
* @protected
* @type {integer}
*/
Phaser.ScaleManager.USER_SCALE = 4;
@ -650,14 +736,14 @@ Phaser.ScaleManager.prototype = {
// Initialize core bounds
Phaser.DOM.getOffset(this.game.canvas, this.offset);
this.dom.getOffset(this.game.canvas, this.offset);
this.bounds.setTo(this.offset.x, this.offset.y, this.width, this.height);
this.setGameSize(this.game.width, this.game.height);
// Don't use updateOrientationState so events are not fired
this.screenOrientation = Phaser.DOM.getScreenOrientation(this.compatibility.orientationFallback);
this.screenOrientation = this.dom.getScreenOrientation(this.compatibility.orientationFallback);
},
@ -723,8 +809,8 @@ Phaser.ScaleManager.prototype = {
this.parentNode = null;
this.parentIsWindow = true;
rect.width = window.innerWidth;
rect.height = window.innerHeight;
rect.width = this.dom.visualBounds.width;
rect.height = this.dom.visualBounds.height;
this.offset.set(0, 0);
}
@ -815,27 +901,41 @@ Phaser.ScaleManager.prototype = {
},
/**
* _Experimental_: Set a User scaling factor. This is only used in the USER_SCALE scaling mode.
* Set a User scaling factor used in the USER_SCALE scaling mode.
*
* @method Phaser.ScaleManager#setGameSize
* @protected
* @param {number} width - Width scaling factor.
* @param {numer} height - Height scaling factor.
* The target canvas size is computed by:
*
* canvas.width = (game.width * hScale) - hTrim
* canvas.height = (game.height * vScale) - vTrim
*
* This method can be used in the {@link Phaser.ScaleManager#setResizeCallback resize callback}.
*
* @method Phaser.ScaleManager#setUserScale
* @param {number} hScale - Horizontal scaling factor.
* @param {numer} vScale - Vertical scaling factor.
* @param {integer} [hTrim=0] - Horizontal trim, applied after scaling.
* @param {integer} [vTrim=0] - Vertical trim, applied after scaling.
*/
setUserScale: function (width, height) {
setUserScale: function (hScale, vScale, hTrim, vTrim) {
this._userScaleFactor.setTo(width, height);
this._userScaleFactor.setTo(hScale, vScale);
this._userScaleTrim.setTo(hTrim | 0, vTrim | 0);
this.queueUpdate(true);
},
/**
* Sets the callback that will be called when the bounds of the Canvas's parent container may have changed.
* Sets the callback that will be invoked before sizing calcualtions.
*
* This is the appropriate place to call `setUserScale` if needing custom dynamic scaling.
*
* The callback is supplied with two arguments `scale` and `parentBounds` where `scale` is the ScaleManager
* and `parentBounds`, a Phaser.Rectangle, is the size of the Parent element.
*
* This callback
* - May be invoked even though the parent container or canvas sizes have not changed
* - Unlike `onSizeChange`, it runs _before_ the canvas is guaranteed to be updated
* - Will be invoked from `preUpdate`, _even when_ the game is paused
* - Will be invoked from `preUpdate`, _even when_ the game is paused
*
* See `onSizeChange` for a better way of reacting to layout updates.
*
@ -932,7 +1032,7 @@ Phaser.ScaleManager.prototype = {
var prevThrottle = this._updateThrottle;
this._updateThrottleReset = prevThrottle >= 400 ? 0 : 100;
Phaser.DOM.getOffset(this.game.canvas, this.offset);
this.dom.getOffset(this.game.canvas, this.offset);
var prevWidth = this._parentBounds.width;
var prevHeight = this._parentBounds.height;
@ -941,13 +1041,13 @@ Phaser.ScaleManager.prototype = {
var boundsChanged = bounds.width !== prevWidth || bounds.height !== prevHeight;
// Always invalidate on a newly detected orientation change
var orientationChanged = this.updateOrientationState(false);
var orientationChanged = this.updateOrientationState();
if (boundsChanged || orientationChanged)
{
if (this.onResize)
{
this.onResize.call(this.onResizeContext, bounds.width, bounds.height);
this.onResize.call(this.onResizeContext, this, bounds);
}
this.updateLayout();
@ -1030,7 +1130,7 @@ Phaser.ScaleManager.prototype = {
// This can be invoked in boot pre-canvas
if (this.game.canvas)
{
Phaser.DOM.getOffset(this.game.canvas, this.offset);
this.dom.getOffset(this.game.canvas, this.offset);
}
this.bounds.setTo(this.offset.x, this.offset.y, this.width, this.height);
@ -1059,10 +1159,7 @@ Phaser.ScaleManager.prototype = {
this.forceLandscape = forceLandscape;
this.forcePortrait = forcePortrait;
if (this.updateOrientationState(true))
{
this.queueUpdate(true);
}
this.queueUpdate(true);
},
@ -1095,16 +1192,20 @@ Phaser.ScaleManager.prototype = {
*
* @method Phaser.ScaleManager#updateOrientationState
* @private
* @param {boolean} [recheckOreientation=false] - Forcing rechecking of [in]correct orientation.
* @return {boolean} True if the orientation state changed which means a forced update is likely required.
*/
updateOrientationState: function (recheckOrientation) {
updateOrientationState: function () {
var previousOrientation = this.screenOrientation;
var previouslyIncorrect = this.incorrectOrientation;
this.screenOrientation = Phaser.DOM.getScreenOrientation(this.compatibility.orientationFallback);
this.screenOrientation = this.dom.getScreenOrientation(this.compatibility.orientationFallback);
this.incorrectOrientation = (this.forceLandscape && !this.isLandscape) ||
(this.forcePortrait && !this.isPortrait);
var changed = previousOrientation !== this.screenOrientation;
var correctnessChanged = previouslyIncorrect !== this.incorrectOrientation;
if (changed)
{
@ -1118,28 +1219,23 @@ Phaser.ScaleManager.prototype = {
}
}
if (changed || recheckOrientation)
{
var wasIncorrect = this.incorrectOrientation;
var incorrectNow = (this.forceLandscape && !this.isLandscape) ||
(this.forcePortrait && !this.isPortrait);
if (wasIncorrect !== incorrectNow) {
this.incorrectOrientation = incorrectNow;
changed = true;
if (incorrectNow)
{
this.enterIncorrectOrientation.dispatch();
}
else
{
this.leaveIncorrectOrientation.dispatch();
}
if (correctnessChanged) {
if (this.incorrectOrientation)
{
this.enterIncorrectOrientation.dispatch();
}
else
{
this.leaveIncorrectOrientation.dispatch();
}
}
return changed;
if (changed || correctnessChanged)
{
this.onOrientationChange.dispatch(this, previousOrientation, previouslyIncorrect);
}
return changed || correctnessChanged;
},
@ -1250,8 +1346,8 @@ Phaser.ScaleManager.prototype = {
}
else if (scaleMode === Phaser.ScaleManager.SHOW_ALL)
{
if (!this.isFullScreen && !this.parentIsWindow &&
this.compatibility.showAllCanExpand)
if (!this.isFullScreen && this.boundingParent &&
this.compatibility.canExpandParent)
{
// Try to expand parent out, but choosing maximizing dimensions.
// Then select minimize dimensions which should then honor parent
@ -1272,11 +1368,23 @@ Phaser.ScaleManager.prototype = {
}
else if (scaleMode === Phaser.ScaleManager.USER_SCALE)
{
this.width = this.game.width * this._userScaleFactor.x;
this.height = this.game.height * this._userScaleFactor.y;
this.width = (this.game.width * this._userScaleFactor.x) - this._userScaleTrim.x;
this.height = (this.game.height * this._userScaleFactor.y) - this._userScaleTrim.y;
}
}
if (!this.compatibility.canExpandParent &&
(scaleMode === Phaser.ScaleManager.SHOW_ALL || scaleMode === Phaser.ScaleManager.USER_SCALE))
{
var bounds = this.getParentBounds();
this.width = Math.min(this.width, bounds.width);
this.height = Math.min(this.height, bounds.height);
}
// Always truncate / force to integer
this.width = this.width | 0;
this.height = this.height | 0;
this.reflowCanvas();
},
@ -1284,32 +1392,32 @@ Phaser.ScaleManager.prototype = {
/**
* Returns the computed Parent size/bounds that the Display canvas is allowed/expected to fill.
*
* If in fullscreen mode or without parent (see {@link Phaser.ScaleManager#parentIsWindow}),
* this will be the bounds of the viewport itself.
* If in fullscreen mode or without parent (see {@link Phaser.ScaleManager#parentIsWindow parentIsWindow}),
* this will be the bounds of the visual viewport itself.
*
* This function takes the `windowConstraints` into consideration - if the parent is partially outside
* the viewport then this function may return a smaller than expected size.
*
* Values are rounded to the nearest pixel.
*
* @method Phaser.ScaleManager#getParentBounds
* @protected
* @param {Phaser.Rectangle} [target=(new Rectangle)] - The rectangle to update; a new one is created as needed.
* @return {Phaser.Rectangle} The established parent bounds.
*/
getParentBounds: function (target) {
var bounds = target || new Phaser.Rectangle();
var parentNode = this.game.canvas && this.game.canvas.parentNode;
var parentNode = this.boundingParent;
var vp = this.dom.visualBounds;
if (this.isFullScreen && !this._createdFullScreenTarget)
if (!parentNode)
{
bounds.setTo(0, 0, window.outerWidth, window.outerHeight);
}
else if (this.parentIsWindow || !parentNode)
{
bounds.setTo(0, 0, window.innerWidth, window.innerHeight);
bounds.setTo(0, 0, vp.width, vp.height);
}
else
{
// Ref. http://msdn.microsoft.com/en-us/library/hh781509(v=vs.85).aspx for getBoundingClientRect
var clientRect = parentNode.getBoundingClientRect();
bounds.setTo(clientRect.left, clientRect.top, clientRect.width, clientRect.height);
@ -1317,11 +1425,11 @@ Phaser.ScaleManager.prototype = {
var wc = this.windowConstraints;
if (wc.right)
{
bounds.right = Math.min(bounds.right, window.innerWidth);
bounds.right = Math.min(bounds.right, vp.width);
}
if (wc.bottom)
{
bounds.bottom = Math.min(bounds.bottom, window.innerHeight);
bounds.bottom = Math.min(bounds.bottom, vp.height);
}
}
@ -1524,8 +1632,8 @@ Phaser.ScaleManager.prototype = {
*/
setMaximum: function () {
this.width = window.innerWidth;
this.height = window.innerHeight;
this.width = this.dom.visualBounds.width;
this.height = this.dom.visualBounds.height;
},
@ -1593,9 +1701,9 @@ Phaser.ScaleManager.prototype = {
* Creates a fullscreen target. This is called automatically as as needed when entering
* fullscreen mode and the resulting element is supplied to `onFullScreenInit`.
*
* Use {@link Phaser.ScaleManager#onFullScreenInit} to customize the created object.
* Use {@link Phaser.ScaleManager#onFullScreenInit onFullScreenInit} to customize the created object.
*
* @method Phaser.ScaleMode#createFullScreenTarget
* @method Phaser.ScaleManager#createFullScreenTarget
* @protected
*/
createFullScreenTarget: function () {
@ -1666,7 +1774,7 @@ Phaser.ScaleManager.prototype = {
var initData = {
targetElement: fsTarget
};
this.onFullScreenInit.dispatch(initData);
this.onFullScreenInit.dispatch(this, initData);
if (this._createdFullScreenTarget)
{
@ -1813,6 +1921,8 @@ Phaser.ScaleManager.prototype = {
this.leaveFullScreen.dispatch(this.width, this.height);
}
this.onFullScreenChange.dispatch(this);
},
/**
@ -1831,45 +1941,7 @@ Phaser.ScaleManager.prototype = {
console.warn('Phaser.ScaleManager: requestFullscreen failed or device does not support the Fullscreen API');
this.fullScreenFailed.dispatch();
},
/**
* A cross-browser element.getBoundingClientRect method with optional cushion.
*
* Returns a plain object containing the properties `top/bottom/left/right/width/height` with respect to the top-left corner of the current viewport.
* Its properties match the native rectangle.
* The cushion parameter is an amount of pixels (+/-) to cushion the element.
* It adjusts the measurements such that it is possible to detect when an element is near the viewport.
*
* @method Phaser.ScaleManager#elementBounds
* @protected
* @param {DOMElement|Object} [element=(Display canvas)] - The element or stack (uses first item) to get the bounds for. If none given it defaults to the Phaser Display canvas.
* @param {number} [cushion] - A +/- pixel adjustment amount.
* @return {Object|boolean} A plain object containing the properties `top/bottom/left/right/width/height` or `false` if a non-valid element is given.
* @see {@link Phaser.DOM.getBounds}
*/
elementBounds: function (element, cushion) {
if (typeof element === 'undefined') { element = this.game.canvas; }
return Phaser.DOM.getBounds(element, cushion);
},
/**
* Get the viewport aspect ratio (or the aspect ratio of an object or element)
* See {@link http://w3.org/TR/css3-mediaqueries/#orientation}.
*
* @method Phaser.ScaleManager#aspect
* @protected
* @param {(DOMElement|Object)} [object=(viewport)] - Optional object. Must have public `width` and `height` properties or methods.
* @return {number} The aspect ratio.
* @see {@link Phaser.DOM.getAspectRatio}
*/
aspect: function (object) {
return Phaser.DOM.getAspectRatio(object);
this.onFullScreenError.dispatch(this);
},
@ -1991,7 +2063,9 @@ Phaser.ScaleManager.prototype.checkOrientation = Phaser.ScaleManager.prototype.o
/**
* Updates the size of the Game or the size/position of the Display canvas based on internal state.
*
* Do not call this to "refresh" the layout - use {@link Phaser.ScaleManager#refresh}.
* Do not call this directly. To "refresh" the layout use {@link Phaser.ScaleManager#refresh refresh}.
* To precisely control the scaling/size, apply appropriate rules to the bounding Parent container or
* use the {@link Phaser.ScaleManager#scaleMode USER_SCALE scale mode}.
*
* @method Phaser.ScaleManager#setScreenSize
* @protected
@ -2002,7 +2076,9 @@ Phaser.ScaleManager.prototype.setScreenSize = Phaser.ScaleManager.prototype.upda
/**
* Updates the size/position of the Display canvas based on internal state.
*
* Do not call this to "refresh" the layout - use {@link Phaser.ScaleManager#refresh}.
* Do not call this directly. To "refresh" the layout use {@link Phaser.ScaleManager#refresh refresh}.
* To precisely control the scaling/size, apply appropriate rules to the bounding Parent container or
* use the {@link Phaser.ScaleManager#scaleMode USER_SCALE scale mode}.
*
* @method setSize
* @memberof Phaser.ScaleManager
@ -2033,6 +2109,30 @@ Phaser.ScaleManager.prototype.checkOrientationState = function () {
};
/**
* The DOM element that is considered the Parent bounding element, if any.
*
* This `null` if `parentIsWindow` is true or if fullscreen mode is entered and `fullScreenTarget` is specified.
* It will also be null if there is no game canvas or if the game canvas has no parent.
*
* @property {?DOMElement} boundingParent
* @readonly
*/
Object.defineProperty(Phaser.ScaleManager.prototype, "boundingParent", {
get: function () {
if (this.parentIsWindow ||
(this.isFullScreen && !this._createdFullScreenTarget))
{
return null;
}
var parentNode = this.game.canvas && this.game.canvas.parentNode;
return parentNode || null;
}
});
/**
* The scaling method used by the ScaleManager when not in fullscreen.
*
@ -2055,13 +2155,15 @@ Phaser.ScaleManager.prototype.checkOrientationState = function () {
* <dd>
* The dimensions of the game display area are changed to match the size of the parent container.
* That is, this mode _changes the Game size_ to match the display size.
*
* <p>
* Any manually set Game size (see `setGameSize`) is ignored while in effect.
* </dd>
* <dt>{@link Phaser.ScaleManager.USER_SCALE}</dt>
* <dd>
* _Experimental_: The Game display area is scaled according to a user-speficied scale.
* Use `setUserScale` to change the scale factor.
* The game Display is scaled according to the user-specified scale set by {@link Phaser.ScaleManager#setUserScale setUserScale}.
* <p>
* This scale can be adjusted in the {@link Phaser.ScaleManager#setResizeCallback resize callback}
* for flexible custom-sizing needs.
* </dd>
* </dl>
*
@ -2098,7 +2200,7 @@ Object.defineProperty(Phaser.ScaleManager.prototype, "scaleMode", {
/**
* The scaling method used by the ScaleManager when in fullscreen.
*
* See {@link Phaser.ScaleManager#scaleMode} for the different modes allowed.
* See {@link Phaser.ScaleManager#scaleMode scaleMode} for the different modes allowed.
*
* @name Phaser.ScaleManager#fullScreenScaleMode
* @property {integer} fullScreenScaleMode
@ -2139,6 +2241,8 @@ Object.defineProperty(Phaser.ScaleManager.prototype, "fullScreenScaleMode", {
/**
* Returns the current scale mode - for normal or fullscreen operation.
*
* See {@link Phaser.ScaleManager#scaleMode scaleMode} for the different modes allowed.
*
* @name Phaser.ScaleManager#currentScaleMode
* @property {number} currentScaleMode
* @protected
@ -2236,7 +2340,8 @@ Object.defineProperty(Phaser.ScaleManager.prototype, "isFullScreen", {
});
/**
* Returns true if the browser dimensions match a portrait display.
* Returns true if the browser is in portrait mode.
*
* @name Phaser.ScaleManager#isPortrait
* @property {boolean} isPortrait
* @readonly
@ -2250,7 +2355,8 @@ Object.defineProperty(Phaser.ScaleManager.prototype, "isPortrait", {
});
/**
* Returns true if the browser dimensions match a landscape display.
* Returns true if the browser is in landscape mode.
*
* @name Phaser.ScaleManager#isLandscape
* @property {boolean} isLandscape
* @readonly

View file

@ -9,13 +9,17 @@
*
* Provides a useful Window and Element functions as well as cross-browser compatibility buffer.
*
* Some code originally derived from {@link https://github.com/ryanve/verge verge}.
*
* @class Phaser.DOM
* @static
*/
Phaser.DOM = {
/**
* Get the DOM offset values of any given element
* Get the [absolute] position of the element relative to the Document.
*
* The value may vary slightly as the page is scrolled due to rounding errors.
*
* @method Phaser.DOM.getOffset
* @param {DOMElement} element - The targeted element that we want to retrieve the offset.
@ -27,24 +31,11 @@ Phaser.DOM = {
point = point || new Phaser.Point();
var box = element.getBoundingClientRect();
var clientTop = element.clientTop || document.body.clientTop || 0;
var clientLeft = element.clientLeft || document.body.clientLeft || 0;
// Without this check Chrome is now throwing console warnings about strict vs. quirks :(
var scrollTop = 0;
var scrollLeft = 0;
if (document.compatMode === 'CSS1Compat')
{
scrollTop = window.pageYOffset || document.documentElement.scrollTop || element.scrollTop || 0;
scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || element.scrollLeft || 0;
}
else
{
scrollTop = window.pageYOffset || document.body.scrollTop || element.scrollTop || 0;
scrollLeft = window.pageXOffset || document.body.scrollLeft || element.scrollLeft || 0;
}
var scrollTop = Phaser.DOM.scrollY;
var scrollLeft = Phaser.DOM.scrollX;
var clientTop = document.documentElement.clientTop;
var clientLeft = document.documentElement.clientLeft;
point.x = box.left + scrollLeft - clientLeft;
point.y = box.top + scrollTop - clientTop;
@ -62,7 +53,7 @@ Phaser.DOM = {
* It adjusts the measurements such that it is possible to detect when an element is near the viewport.
*
* @method Phaser.DOM.getBounds
* @param {DOMElement|Object} element - The element or stack (uses first item) to get the bounds for. If none given it defaults to the Phaser game canvas.
* @param {DOMElement|Object} element - The element or stack (uses first item) to get the bounds for.
* @param {number} [cushion] - A +/- pixel adjustment amount.
* @return {Object|boolean} A plain object containing the properties `top/bottom/left/right/width/height` or `false` if a non-valid element is given.
*/
@ -106,16 +97,15 @@ Phaser.DOM = {
},
/**
* Get the viewport aspect ratio (or the aspect ratio of an object or element)
* @link http://w3.org/TR/css3-mediaqueries/#orientation
* Get the Visual viewport aspect ratio (or the aspect ratio of an object or element)
*
* @method Phaser.DOM.getAspectRatio
* @param {(DOMElement|Object)} [object=(viewport)] - Optional object. Must have public `width` and `height` properties or methods.
* @param {(DOMElement|Object)} [object=(visualViewport)] - The object to determine the aspect ratio for. Must have public `width` and `height` properties or methods.
* @return {number} The aspect ratio.
*/
getAspectRatio: function (object) {
object = null == object ? this.getViewport() : 1 === object.nodeType ? this.getElementBounds(object) : object;
object = null == object ? this.visualBounds : 1 === object.nodeType ? this.getBounds(object) : object;
var w = object['width'];
var h = object['height'];
@ -135,22 +125,7 @@ Phaser.DOM = {
},
/**
* Get the viewport dimensions.
*
* @method Phaser.DOM#getViewport
* @protected
*/
getViewport: function () {
return {
width: this.viewportWidth,
height: this.viewportHeight
};
},
/**
* Tests if the given DOM element is within the viewport.
* Tests if the given DOM element is within the Layout viewport.
*
* The optional cushion parameter allows you to specify a distance.
*
@ -162,11 +137,11 @@ Phaser.DOM = {
* @param {number} [cushion] - The cushion allows you to specify a distance within which the element must be within the viewport.
* @return {boolean} True if the element is within the viewport, or within `cushion` distance from it.
*/
inViewport: function (element, cushion) {
inLayoutViewport: function (element, cushion) {
var r = this.getElementBounds(element, cushion);
var r = this.getBounds(element, cushion);
return !!r && r.bottom >= 0 && r.right >= 0 && r.top <= this.viewportWidth && r.left <= this.viewportHeight;
return !!r && r.bottom >= 0 && r.right >= 0 && r.top <= this.layoutBounds.width && r.left <= this.layoutBounds.height;
},
@ -179,7 +154,7 @@ Phaser.DOM = {
* - Screen Orientation API, or variation of - Future track. Most desktop and mobile browsers.
* - Screen size ratio check - If fallback is 'screen', suited for desktops.
* - Viewport size ratio check - If fallback is 'viewport', suited for mobile.
* - window.orientation - If fallback is 'window.orientation', non-recommended track.
* - window.orientation - If fallback is 'window.orientation', works iOS and probably most Android; non-recommended track.
* - Media query
* - Viewport size ratio check (probably only IE9 and legacy mobile gets here..)
*
@ -220,7 +195,7 @@ Phaser.DOM = {
}
else if (primaryFallback === 'viewport')
{
return (this.viewportHeight > this.viewportWidth) ? PORTRAIT : LANDSCAPE;
return (this.visualBounds.height > this.visualBounds.width) ? PORTRAIT : LANDSCAPE;
}
else if (primaryFallback === 'window.orientation' && typeof window.orientation === 'number')
{
@ -239,120 +214,206 @@ Phaser.DOM = {
}
}
return (this.viewportHeight > this.viewportWidth) ? PORTRAIT : LANDSCAPE;
return (this.visualBounds.height > this.visualBounds.width) ? PORTRAIT : LANDSCAPE;
}
},
/**
* The bounds of the Visual viewport, as discussed in
* {@link http://www.quirksmode.org/mobile/viewports.html A tale of two viewports — part one}
* with one difference: the viewport size _excludes_ scrollbars, as found on some desktop browsers.
*
* Supported mobile:
* iOS/Safari, Android 4, IE10, Firefox OS (maybe not Firefox Android), Opera Mobile 16
*
* The properties change dynamically.
*
* @type {Phaser.Rectangle}
* @property {number} x - Scroll, left offset - eg. "scrollX"
* @property {number} y - Scroll, top offset - eg. "scrollY"
* @property {number} width - Viewport width in pixels.
* @property {number} height - Viewport height in pixels.
* @readonly
*/
visualBounds: new Phaser.Rectangle(),
/**
* The bounds of the Layout viewport, as discussed in
* {@link http://www.quirksmode.org/mobile/viewports2.html A tale of two viewports — part two};
* but honoring the constraints as specified applicable viewport meta-tag.
*
* The bounds returned are not guaranteed to be fully aligned with CSS media queries (see
* {@link http://www.matanich.com/2013/01/07/viewport-size/ What size is my viewport?}).
*
* This is _not_ representative of the Visual bounds: in particular the non-primary axis will
* generally be significantly larger than the screen height on mobile devices when running with a
* constrained viewport.
*
* The properties change dynamically.
*
* @type {Phaser.Rectangle}
* @property {number} width - Viewport width in pixels.
* @property {number} height - Viewport height in pixels.
* @readonly
*/
layoutBounds: new Phaser.Rectangle(),
/**
* The size of the document / Layout viewport.
*
* This incorrectly reports the dimensions in IE.
*
* The properties change dynamically.
*
* @type {Phaser.Rectangle}
* @property {number} width - Document width in pixels.
* @property {number} height - Document height in pixels.
* @readonly
*/
documentBounds: new Phaser.Rectangle()
};
/**
* A cross-browser window.scrollX.
*
* @name Phaser.DOM.scrollX
* @property {number} scrollX
* @readonly
* @protected
*/
Object.defineProperty(Phaser.DOM, "scrollX", {
Phaser.Device.whenReady(function (device) {
get: function () {
return window.pageXOffset || document.documentElement.scrollLeft;
}
// All target browsers should support page[XY]Offset.
var scrollX = window && ('pageXOffset' in window) ?
function () { return window.pageXOffset; } :
function () { return document.documentElement.scrollLeft; };
});
var scrollY = window && ('pageYOffset' in window) ?
function () { return window.pageYOffset; } :
function () { return document.documentElement.scrollTop; };
/**
* A cross-browser window.scrollY.
*
* @name Phaser.DOM.scrollY
* @property {number} scrollY
* @readonly
* @protected
*/
Object.defineProperty(Phaser.DOM, "scrollY", {
/**
* A cross-browser window.scrollX.
*
* @name Phaser.DOM.scrollX
* @property {number} scrollX
* @readonly
* @protected
*/
Object.defineProperty(Phaser.DOM, "scrollX", {
get: scrollX
});
get: function () {
return window.pageYOffset || document.documentElement.scrollTop;
}
/**
* A cross-browser window.scrollY.
*
* @name Phaser.DOM.scrollY
* @property {number} scrollY
* @readonly
* @protected
*/
Object.defineProperty(Phaser.DOM, "scrollY", {
get: scrollY
});
});
// For Phaser.DOM.visualBounds
// Ref. http://quirksmode.org/mobile/tableViewport.html
/**
* Gets the viewport width in pixels.
*
* @name Phaser.DOM.viewportWidth
* @property {number} viewportWidth
* @readonly
* @protected
*/
Object.defineProperty(Phaser.DOM, "viewportWidth", {
Object.defineProperty(Phaser.DOM.visualBounds, "x", {
get: scrollX
});
get: function () {
Object.defineProperty(Phaser.DOM.visualBounds, "y", {
get: scrollY
});
var a = document.documentElement.clientWidth;
var b = window.innerWidth;
// Desktop browsers align the layout viewport with the visual viewport.
// This differs from mobile browsers with their zooming design.
if (device.desktop &&
(document.documentElement.clientWidth <= window.innerWidth) &&
(document.documentElement.clientHeight <= window.innerHeight))
{
return a < b ? b : a;
Object.defineProperty(Phaser.DOM.visualBounds, "width", {
get: function () {
return document.documentElement.clientWidth;
}
});
Object.defineProperty(Phaser.DOM.visualBounds, "height", {
get: function () {
return document.documentElement.clientHeight;
}
});
} else {
Object.defineProperty(Phaser.DOM.visualBounds, "width", {
get: function () {
return window.innerWidth;
}
});
Object.defineProperty(Phaser.DOM.visualBounds, "height", {
get: function () {
return window.innerHeight;
}
});
}
});
// For Phaser.DOM.layoutBounds
/**
* Gets the viewport height in pixels.
*
* @name Phaser.DOM.viewportHeight
* @property {number} viewportHeight
* @readonly
* @protected
*/
Object.defineProperty(Phaser.DOM, "viewportHeight", {
Object.defineProperty(Phaser.DOM.layoutBounds, "x", {
value: 0
});
get: function () {
Object.defineProperty(Phaser.DOM.layoutBounds, "y", {
value: 0
});
var a = document.documentElement.clientHeight;
var b = window.innerHeight;
Object.defineProperty(Phaser.DOM.layoutBounds, "width", {
return a < b ? b : a;
get: function () {
var a = document.documentElement.clientWidth;
var b = window.innerWidth;
}
return a < b ? b : a; // max
}
});
});
/**
* Gets the document width in pixels.
*
* @name Phaser.DOM.documentWidth
* @property {number} documentWidth
* @readonly
* @protected
*/
Object.defineProperty(Phaser.DOM, "documentWidth", {
Object.defineProperty(Phaser.DOM.layoutBounds, "height", {
get: function () {
get: function () {
var a = document.documentElement.clientHeight;
var b = window.innerHeight;
var d = document.documentElement;
return Math.max(d.clientWidth, d.offsetWidth, d.scrollWidth);
return a < b ? b : a; // max
}
}
});
});
// For Phaser.DOM.documentBounds
// Ref. http://www.quirksmode.org/mobile/tableViewport_desktop.html
/**
* Gets the document height in pixels.
*
* @name Phaser.DOM.documentHeight
* @property {number} documentHeight
* @readonly
* @protected
*/
Object.defineProperty(Phaser.DOM, "documentHeight", {
Object.defineProperty(Phaser.DOM.documentBounds, "x", {
value: 0
});
get: function () {
Object.defineProperty(Phaser.DOM.documentBounds, "y", {
value: 0
});
var d = document.documentElement;
return Math.max(d.clientHeight, d.offsetHeight, d.scrollHeight);
Object.defineProperty(Phaser.DOM.documentBounds, "width", {
}
get: function () {
var d = document.documentElement;
return Math.max(d.clientWidth, d.offsetWidth, d.scrollWidth);
}
});
});
Object.defineProperty(Phaser.DOM.documentBounds, "height", {
get: function () {
var d = document.documentElement;
return Math.max(d.clientHeight, d.offsetHeight, d.scrollHeight);
}
});
}, null, true);

View file

@ -465,25 +465,27 @@ Phaser.Device.onInitialized = new Phaser.Signal();
* if the device is already "ready". See {@link Phaser.Device#deviceReadyAt deviceReadyAt}.
*
* @method
* @param {function} handler - Callback to invoke when the device is ready
* @param {function} handler - Callback to invoke when the device is ready. It is invoked with the given context the Phaser.Device object is supplied as the first argument.
* @param {object} [context] - Context in which to invoke the handler
* @param {boolean} [nonPrimer=false] - If true the device ready check will not be started.
*/
Phaser.Device.whenReady = function (callback, context) {
Phaser.Device.whenReady = function (callback, context, nonPrimer) {
var readyCheck = this._readyCheck;
if (this.deviceReadyAt || !readyCheck)
{
callback.call(context);
callback.call(context, this);
}
else if (readyCheck._monitor)
else if (readyCheck._monitor || nonPrimer)
{
readyCheck._queue = readyCheck._queue || [];
readyCheck._queue.push([callback, context]);
}
else
{
readyCheck._monitor = readyCheck.bind(this);
readyCheck._queue = [];
readyCheck._queue = readyCheck._queue || [];
readyCheck._queue.push([callback, context]);
var cordova = typeof window.cordova !== 'undefined';
@ -542,7 +544,7 @@ Phaser.Device._readyCheck = function () {
{
var callback = item[0];
var context = item[1];
callback.call(context);
callback.call(context, this);
}
// Remove no longer useful methods and properties.
@ -611,7 +613,9 @@ Phaser.Device._initialize = function () {
}
}
if (device.windows || device.macOS || (device.linux && device.silk === false) || device.chromeOS)
var silk = /Silk/.test(ua); // detected in browsers
if (device.windows || device.macOS || (device.linux && !silk) || device.chromeOS)
{
device.desktop = true;
}

View file

@ -58,9 +58,9 @@
"src/gameobjects/RetroFont.js",
"src/gameobjects/Particle.js",
"src/system/Device.js",
"src/system/DOM.js",
"src/system/Canvas.js",
"src/system/Device.js",
"src/system/RequestAnimationFrame.js",
"src/math/Math.js",