diff --git a/README.md b/README.md
index b0f02abc..5950f3e3 100644
--- a/README.md
+++ b/README.md
@@ -131,6 +131,7 @@ Updates:
* Loader.setPreloadSprite() will now set sprite.visible = true once the crop has been applied. Should help avoid issues (#430) on super-slow connections.
* Updated the way the page visibility is checked, should now be more compatible across more browsers.
* Phaser.Input.Key.isUp now defaults to 'true', as does GamepadButton.isUp (#474)
+* Vastly improved visibility API support + pageshow/pagehide + focus/blur. Working across Chrome, IE, Firefox, iOS, Android (also fixes #161)
Bug Fixes:
diff --git a/examples/wip/index.php b/examples/wip/index.php
index 3c5511da..37c4f2d1 100644
--- a/examples/wip/index.php
+++ b/examples/wip/index.php
@@ -77,7 +77,7 @@
phaser
-
+
+
+
+
+
+
+
+
diff --git a/src/core/Game.js b/src/core/Game.js
index 0366f248..83c09b93 100644
--- a/src/core/Game.js
+++ b/src/core/Game.js
@@ -86,20 +86,6 @@ Phaser.Game = function (width, height, renderer, parent, state, transparent, ant
*/
this.state = null;
- /**
- * @property {boolean} _paused - Is game paused?
- * @private
- * @default
- */
- this._paused = false;
-
- /**
- * @property {boolean} _loadComplete - Whether load complete loading or not.
- * @private
- * @default
- */
- this._loadComplete = false;
-
/**
* @property {boolean} isBooted - Whether the game engine is booted, aka available.
* @default
@@ -246,6 +232,27 @@ Phaser.Game = function (width, height, renderer, parent, state, transparent, ant
*/
this.stepCount = 0;
+ /**
+ * @property {boolean} _paused - Is game paused?
+ * @private
+ * @default
+ */
+ this._paused = false;
+
+ /**
+ * @property {boolean} _codePaused - Was the game paused via code or a visibility change?
+ * @private
+ * @default
+ */
+ this._codePaused = false;
+
+ /**
+ * @property {boolean} _loadComplete - Whether load complete loading or not.
+ * @private
+ * @default
+ */
+ this._loadComplete = false;
+
// Parse the configuration object (if any)
if (arguments.length === 1 && typeof arguments[0] === 'object')
{
@@ -605,9 +612,16 @@ Phaser.Game.prototype = {
if (this._paused)
{
- this.renderer.render(this.stage);
- this.plugins.render();
- this.state.render();
+ this.input.update();
+
+ if (this.renderType !== Phaser.HEADLESS)
+ {
+ this.renderer.render(this.stage);
+ this.plugins.render();
+ this.state.render();
+
+ this.plugins.postRender();
+ }
}
else
{
@@ -708,6 +722,43 @@ Phaser.Game.prototype = {
this.world = null;
this.isBooted = false;
+ },
+
+ /**
+ * Called by the Stage visibility handler.
+ *
+ * @method Phaser.Game#gamePaused
+ */
+ gamePaused: function (time) {
+
+ // If the game is already paused it was done via game code, so don't re-pause it
+ if (!this._paused)
+ {
+ this._paused = true;
+ this.time.gamePaused(time);
+ this.sound.mute = true;
+ this.onPause.dispatch(this);
+ }
+
+ },
+
+ /**
+ * Called by the Stage visibility handler.
+ *
+ * @method Phaser.Game#gameResumed
+ */
+ gameResumed: function (time) {
+
+ // Game is paused, but wasn't paused via code, so resume it
+ if (this._paused && !this._codePaused)
+ {
+ this._paused = false;
+ this.time.gameResumed(time);
+ this.input.reset();
+ this.sound.mute = false;
+ this.onResume.dispatch(this);
+ }
+
}
};
@@ -733,6 +784,9 @@ Object.defineProperty(Phaser.Game.prototype, "paused", {
if (this._paused === false)
{
this._paused = true;
+ this._codePaused = true;
+ this.sound.mute = true;
+ this.time.gamePaused();
this.onPause.dispatch(this);
}
}
@@ -741,7 +795,10 @@ Object.defineProperty(Phaser.Game.prototype, "paused", {
if (this._paused)
{
this._paused = false;
+ this._codePaused = false;
this.input.reset();
+ this.sound.mute = false;
+ this.time.gameResumed();
this.onResume.dispatch(this);
}
}
diff --git a/src/core/Stage.js b/src/core/Stage.js
index f9661c1f..db8c4de2 100644
--- a/src/core/Stage.js
+++ b/src/core/Stage.js
@@ -246,27 +246,37 @@ Phaser.Stage.prototype.boot = function () {
*/
Phaser.Stage.prototype.checkVisibility = function () {
- var supportsVisibilityApi = false;
- var prefixes = [ "", "moz", "ms", "webkit" ];
-
- while (prefixes.length)
+ if (document.webkitHidden != undefined)
{
- prefix = prefixes.pop();
- this._hiddenVar = prefix ? prefix + "Hidden" : "hidden";
-
- if (this._hiddenVar in document)
- {
- supportsVisibilityApi = true;
- break;
- }
+ this._hiddenVar = 'webkitvisibilitychange';
+ }
+ else if (document.mozHidden != undefined)
+ {
+ this._hiddenVar = 'mozvisibilitychange';
+ }
+ else if (document.msHidden != undefined)
+ {
+ this._hiddenVar = 'msvisibilitychange';
+ }
+ else if (document.hidden != undefined)
+ {
+ this._hiddenVar = 'visibilitychange';
+ }
+ else
+ {
+ this._hiddenVar = null;
}
// Does browser support it? If not (like in IE9 or old Android) we need to fall back to blur/focus
- if (supportsVisibilityApi)
+ if (this._hiddenVar)
{
document.addEventListener(this._hiddenVar, this._onChange, false);
- document.addEventListener('pagehide', this._onChange, false);
- document.addEventListener('pageshow', this._onChange, false);
+ }
+
+ if (window['onpagehide'])
+ {
+ window.onpagehide = this._onChange;
+ window.onpageshow = this._onChange;
}
window.onblur = this._onChange;
@@ -304,13 +314,27 @@ Phaser.Stage.prototype.visibilityChange = function (event) {
return;
}
- if (this.game.paused === false && (event.type === 'pagehide' || event.type === 'blur' || document[this._hiddenVar] === true))
+ if (event.type === 'pagehide' || event.type === 'blur' || event.type === 'pageshow' || event.type === 'focus')
{
- this.game.paused = true;
+ if (event.type === 'pagehide' || event.type === 'blur')
+ {
+ this.game.gamePaused(event.timeStamp);
+ }
+ else if (event.type === 'pageshow' || event.type === 'focus')
+ {
+ this.game.gameResumed(event.timeStamp);
+ }
+
+ return;
+ }
+
+ if (document.hidden || document.mozHidden || document.msHidden || document.webkitHidden)
+ {
+ this.game.gamePaused(event.timeStamp);
}
else
{
- this.game.paused = false;
+ this.game.gameResumed(event.timeStamp);
}
}
diff --git a/src/input/Input.js b/src/input/Input.js
index 774f67c2..40967a4b 100644
--- a/src/input/Input.js
+++ b/src/input/Input.js
@@ -457,6 +457,7 @@ Phaser.Input.prototype = {
if (this.pointer10) { this.pointer10.update(); }
this._pollCounter = 0;
+
},
/**
diff --git a/src/input/Keyboard.js b/src/input/Keyboard.js
index 17e5eb80..a738aa5d 100644
--- a/src/input/Keyboard.js
+++ b/src/input/Keyboard.js
@@ -71,7 +71,7 @@ Phaser.Keyboard = function (game) {
Phaser.Keyboard.prototype = {
/**
- * Add callbacks to the Keyboard handler so that each time a key is pressed down or releases the callbacks are activated.
+ * Add callbacks to the Keyboard handler so that each time a key is pressed down or released the callbacks are activated.
*
* @method Phaser.Keyboard#addCallbacks
* @param {Object} context - The context under which the callbacks are run.
diff --git a/src/input/Pointer.js b/src/input/Pointer.js
index 20ec3e86..874e69f6 100644
--- a/src/input/Pointer.js
+++ b/src/input/Pointer.js
@@ -209,8 +209,8 @@ Phaser.Pointer.prototype = {
// Fix to stop rogue browser plugins from blocking the visibility state event
if (this.game.stage.disableVisibilityChange === false && this.game.paused && this.game.scale.incorrectOrientation === false)
{
- this.game.paused = false;
- return this;
+ // this.game.paused = false;
+ // return this;
}
this._history.length = 0;
diff --git a/src/time/Time.js b/src/time/Time.js
index f3fe853a..f71ba1f3 100644
--- a/src/time/Time.js
+++ b/src/time/Time.js
@@ -143,8 +143,8 @@ Phaser.Time = function (game) {
this._i = 0;
// Listen for game pause/resume events
- this.game.onPause.add(this.gamePaused, this);
- this.game.onResume.add(this.gameResumed, this);
+ // this.game.onPause.add(this.gamePaused, this);
+ // this.game.onResume.add(this.gameResumed, this);
};
@@ -218,6 +218,7 @@ Phaser.Time.prototype = {
this.elapsed = this.now - this.time;
+ /*
this.msMin = this.game.math.min(this.msMin, this.elapsed);
this.msMax = this.game.math.max(this.msMax, this.elapsed);
@@ -231,9 +232,12 @@ Phaser.Time.prototype = {
this._timeLastSecond = this.now;
this.frames = 0;
}
+ */
this.time = this.now;
this.lastTime = time + this.timeToCall;
+
+ /*
this.physicsElapsed = 1.0 * (this.elapsed / 1000);
// Clamp the delta
@@ -241,11 +245,12 @@ Phaser.Time.prototype = {
{
this.physicsElapsed = 0.05;
}
+ */
- // Paused?
+ // Paused but still running?
if (this.game.paused)
{
- this.pausedTime = this.now - this._pauseStarted;
+ // this.pausedTime = this.now - this._pauseStarted;
}
else
{
@@ -278,13 +283,22 @@ Phaser.Time.prototype = {
* @method Phaser.Time#gamePaused
* @private
*/
- gamePaused: function () {
+ gamePaused: function (time) {
- this._pauseStarted = this.now;
+ if (typeof time === 'undefined')
+ {
+ this._pauseStarted = this.now;
+ }
+ else
+ {
+ this._pauseStarted = time;
+ }
this.events.pause();
- for (var i = 0; i < this._timers.length; i++)
+ var i = this._timers.length;
+
+ while (i--)
{
this._timers[i].pause();
}
@@ -296,11 +310,21 @@ Phaser.Time.prototype = {
* @method Phaser.Time#gameResumed
* @private
*/
- gameResumed: function () {
+ gameResumed: function (time) {
+
+ if (typeof time === 'undefined')
+ {
+ this.pauseDuration = this.now - this._pauseStarted;
+ }
+ else
+ {
+ this.pauseDuration = time - this._pauseStarted;
+ }
// Level out the elapsed timer to avoid spikes
+
this.time = Date.now();
- this.pauseDuration = this.pausedTime;
+
this._justResumed = true;
},