Skip to content

Commit 415342d

Browse files
committed
Vastly improved visibility API support + pageshow/pagehide + focus/blur. Working across Chrome, IE, Firefox, iOS, Android (also fixes phaserjs#161)
1 parent beaac18 commit 415342d

10 files changed

Lines changed: 246 additions & 48 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ Updates:
131131
* Loader.setPreloadSprite() will now set sprite.visible = true once the crop has been applied. Should help avoid issues (#430) on super-slow connections.
132132
* Updated the way the page visibility is checked, should now be more compatible across more browsers.
133133
* Phaser.Input.Key.isUp now defaults to 'true', as does GamepadButton.isUp (#474)
134+
* Vastly improved visibility API support + pageshow/pagehide + focus/blur. Working across Chrome, IE, Firefox, iOS, Android (also fixes #161)
134135

135136

136137
Bug Fixes:

examples/wip/index.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ function printJSLinks($dir, $files) {
7777
<meta charset="UTF-8" />
7878
<meta name="viewport" content="initial-scale=1 maximum-scale=1 user-scalable=0 minimal-ui" />
7979
<title>phaser</title>
80-
<base href="../"></base>
80+
<base href="../" />
8181
<script src="_site/js/jquery-2.0.3.min.js" type="text/javascript"></script>
8282
<?php
8383
require('../../build/config.php');

examples/wip/tab swap.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
2+
var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render });
3+
4+
function preload() {
5+
6+
game.load.spritesheet('mummy', 'assets/sprites/metalslug_mummy37x45.png', 37, 45, 18);
7+
game.load.audio('boden', ['assets/audio/bodenstaendig_2000_in_rock_4bit.mp3', 'assets/audio/bodenstaendig_2000_in_rock_4bit.ogg']);
8+
9+
}
10+
11+
var mummy;
12+
var anim;
13+
var music;
14+
var s = [];
15+
16+
function create() {
17+
18+
game.stage.backgroundColor = 0x3d4d3d;
19+
20+
music = game.add.audio('boden');
21+
music.play();
22+
23+
mummy = game.add.sprite(500, 300, 'mummy', 5);
24+
mummy.scale.set(2);
25+
26+
anim = mummy.animations.add('walk');
27+
28+
anim.play(10, true);
29+
30+
game.onPause.add(paused, this);
31+
game.onResume.add(resumed, this);
32+
33+
var space = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
34+
space.onDown.add(pauseToggle, this);
35+
36+
s.push('starting: ' + game.stage._hiddenVar);
37+
38+
}
39+
40+
function pauseToggle() {
41+
42+
if (game.paused)
43+
{
44+
game.paused = false;
45+
}
46+
else
47+
{
48+
game.paused = true;
49+
}
50+
51+
}
52+
53+
function paused() {
54+
55+
s.push('paused now: ' + game.time.now);
56+
// console.log('paused now:', game.time.now);
57+
58+
}
59+
60+
function resumed() {
61+
62+
s.push('resumed now: ' + game.time.now);
63+
s.push('pause duration: ' + game.time.pauseDuration);
64+
// console.log('resumed now:', game.time.now);
65+
// console.log('resumed duration:', game.time.pauseDuration);
66+
67+
}
68+
69+
function update() {
70+
71+
}
72+
73+
function render() {
74+
75+
// game.debug.renderText(anim.frame + ' / 17', 32, 32);
76+
77+
for (var i = 0; i < s.length; i++)
78+
{
79+
game.debug.renderText(s[i], 16, 160 + (16 * i));
80+
}
81+
82+
game.debug.renderSoundInfo(music, 20, 32);
83+
84+
}

resources/Project Templates/Full Screen Mobile/index.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,20 @@
1616
<meta name="apple-mobile-web-app-title" content="Phaser App">
1717
<meta name="viewport" content="initial-scale=1 maximum-scale=1 user-scalable=0 minimal-ui" />
1818
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
19+
<!-- non-retina iPhone pre iOS 7 -->
1920
<link rel="apple-touch-icon" sizes="57x57" href="icons/app_icon_57x57.png">
2021
<link rel="apple-touch-icon" sizes="60x60" href="icons/app_icon_60x60.png">
22+
<!-- non-retina iPad pre iOS 7 -->
2123
<link rel="apple-touch-icon" sizes="72x72" href="icons/app_icon_72x72.png">
24+
<!-- non-retina iPad iOS 7 -->
2225
<link rel="apple-touch-icon" sizes="76x76" href="icons/app_icon_76x76.png">
26+
<!-- retina iPhone pre iOS 7 -->
2327
<link rel="apple-touch-icon" sizes="114x114" href="icons/app_icon_114x114.png">
28+
<!-- retina iPhone iOS 7 -->
2429
<link rel="apple-touch-icon" sizes="120x120" href="icons/app_icon_120x120.png">
30+
<!-- retina iPad pre iOS 7 -->
2531
<link rel="apple-touch-icon" sizes="144x144" href="icons/app_icon_144x144.png">
32+
<!-- retina iPad iOS 7 -->
2633
<link rel="apple-touch-icon" sizes="152x152" href="icons/app_icon_152x152.png">
2734
<link rel="apple-touch-icon" sizes="256x256" href="icons/app_icon_256x256.png">
2835
<link rel="apple-touch-icon" sizes="512x512" href="icons/app_icon_512x512.png">

src/core/Game.js

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -86,20 +86,6 @@ Phaser.Game = function (width, height, renderer, parent, state, transparent, ant
8686
*/
8787
this.state = null;
8888

89-
/**
90-
* @property {boolean} _paused - Is game paused?
91-
* @private
92-
* @default
93-
*/
94-
this._paused = false;
95-
96-
/**
97-
* @property {boolean} _loadComplete - Whether load complete loading or not.
98-
* @private
99-
* @default
100-
*/
101-
this._loadComplete = false;
102-
10389
/**
10490
* @property {boolean} isBooted - Whether the game engine is booted, aka available.
10591
* @default
@@ -246,6 +232,27 @@ Phaser.Game = function (width, height, renderer, parent, state, transparent, ant
246232
*/
247233
this.stepCount = 0;
248234

235+
/**
236+
* @property {boolean} _paused - Is game paused?
237+
* @private
238+
* @default
239+
*/
240+
this._paused = false;
241+
242+
/**
243+
* @property {boolean} _codePaused - Was the game paused via code or a visibility change?
244+
* @private
245+
* @default
246+
*/
247+
this._codePaused = false;
248+
249+
/**
250+
* @property {boolean} _loadComplete - Whether load complete loading or not.
251+
* @private
252+
* @default
253+
*/
254+
this._loadComplete = false;
255+
249256
// Parse the configuration object (if any)
250257
if (arguments.length === 1 && typeof arguments[0] === 'object')
251258
{
@@ -605,9 +612,16 @@ Phaser.Game.prototype = {
605612

606613
if (this._paused)
607614
{
608-
this.renderer.render(this.stage);
609-
this.plugins.render();
610-
this.state.render();
615+
this.input.update();
616+
617+
if (this.renderType !== Phaser.HEADLESS)
618+
{
619+
this.renderer.render(this.stage);
620+
this.plugins.render();
621+
this.state.render();
622+
623+
this.plugins.postRender();
624+
}
611625
}
612626
else
613627
{
@@ -708,6 +722,43 @@ Phaser.Game.prototype = {
708722
this.world = null;
709723
this.isBooted = false;
710724

725+
},
726+
727+
/**
728+
* Called by the Stage visibility handler.
729+
*
730+
* @method Phaser.Game#gamePaused
731+
*/
732+
gamePaused: function (time) {
733+
734+
// If the game is already paused it was done via game code, so don't re-pause it
735+
if (!this._paused)
736+
{
737+
this._paused = true;
738+
this.time.gamePaused(time);
739+
this.sound.mute = true;
740+
this.onPause.dispatch(this);
741+
}
742+
743+
},
744+
745+
/**
746+
* Called by the Stage visibility handler.
747+
*
748+
* @method Phaser.Game#gameResumed
749+
*/
750+
gameResumed: function (time) {
751+
752+
// Game is paused, but wasn't paused via code, so resume it
753+
if (this._paused && !this._codePaused)
754+
{
755+
this._paused = false;
756+
this.time.gameResumed(time);
757+
this.input.reset();
758+
this.sound.mute = false;
759+
this.onResume.dispatch(this);
760+
}
761+
711762
}
712763

713764
};
@@ -733,6 +784,9 @@ Object.defineProperty(Phaser.Game.prototype, "paused", {
733784
if (this._paused === false)
734785
{
735786
this._paused = true;
787+
this._codePaused = true;
788+
this.sound.mute = true;
789+
this.time.gamePaused();
736790
this.onPause.dispatch(this);
737791
}
738792
}
@@ -741,7 +795,10 @@ Object.defineProperty(Phaser.Game.prototype, "paused", {
741795
if (this._paused)
742796
{
743797
this._paused = false;
798+
this._codePaused = false;
744799
this.input.reset();
800+
this.sound.mute = false;
801+
this.time.gameResumed();
745802
this.onResume.dispatch(this);
746803
}
747804
}

src/core/Stage.js

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -246,27 +246,37 @@ Phaser.Stage.prototype.boot = function () {
246246
*/
247247
Phaser.Stage.prototype.checkVisibility = function () {
248248

249-
var supportsVisibilityApi = false;
250-
var prefixes = [ "", "moz", "ms", "webkit" ];
251-
252-
while (prefixes.length)
249+
if (document.webkitHidden != undefined)
253250
{
254-
prefix = prefixes.pop();
255-
this._hiddenVar = prefix ? prefix + "Hidden" : "hidden";
256-
257-
if (this._hiddenVar in document)
258-
{
259-
supportsVisibilityApi = true;
260-
break;
261-
}
251+
this._hiddenVar = 'webkitvisibilitychange';
252+
}
253+
else if (document.mozHidden != undefined)
254+
{
255+
this._hiddenVar = 'mozvisibilitychange';
256+
}
257+
else if (document.msHidden != undefined)
258+
{
259+
this._hiddenVar = 'msvisibilitychange';
260+
}
261+
else if (document.hidden != undefined)
262+
{
263+
this._hiddenVar = 'visibilitychange';
264+
}
265+
else
266+
{
267+
this._hiddenVar = null;
262268
}
263269

264270
// Does browser support it? If not (like in IE9 or old Android) we need to fall back to blur/focus
265-
if (supportsVisibilityApi)
271+
if (this._hiddenVar)
266272
{
267273
document.addEventListener(this._hiddenVar, this._onChange, false);
268-
document.addEventListener('pagehide', this._onChange, false);
269-
document.addEventListener('pageshow', this._onChange, false);
274+
}
275+
276+
if (window['onpagehide'])
277+
{
278+
window.onpagehide = this._onChange;
279+
window.onpageshow = this._onChange;
270280
}
271281

272282
window.onblur = this._onChange;
@@ -304,13 +314,27 @@ Phaser.Stage.prototype.visibilityChange = function (event) {
304314
return;
305315
}
306316

307-
if (this.game.paused === false && (event.type === 'pagehide' || event.type === 'blur' || document[this._hiddenVar] === true))
317+
if (event.type === 'pagehide' || event.type === 'blur' || event.type === 'pageshow' || event.type === 'focus')
318+
{
319+
if (event.type === 'pagehide' || event.type === 'blur')
320+
{
321+
this.game.gamePaused(event.timeStamp);
322+
}
323+
else if (event.type === 'pageshow' || event.type === 'focus')
324+
{
325+
this.game.gameResumed(event.timeStamp);
326+
}
327+
328+
return;
329+
}
330+
331+
if (document.hidden || document.mozHidden || document.msHidden || document.webkitHidden)
308332
{
309-
this.game.paused = true;
333+
this.game.gamePaused(event.timeStamp);
310334
}
311335
else
312336
{
313-
this.game.paused = false;
337+
this.game.gameResumed(event.timeStamp);
314338
}
315339

316340
}

src/input/Input.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ Phaser.Input.prototype = {
457457
if (this.pointer10) { this.pointer10.update(); }
458458

459459
this._pollCounter = 0;
460+
460461
},
461462

462463
/**

src/input/Keyboard.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Phaser.Keyboard = function (game) {
7171
Phaser.Keyboard.prototype = {
7272

7373
/**
74-
* Add callbacks to the Keyboard handler so that each time a key is pressed down or releases the callbacks are activated.
74+
* Add callbacks to the Keyboard handler so that each time a key is pressed down or released the callbacks are activated.
7575
*
7676
* @method Phaser.Keyboard#addCallbacks
7777
* @param {Object} context - The context under which the callbacks are run.

src/input/Pointer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,8 @@ Phaser.Pointer.prototype = {
209209
// Fix to stop rogue browser plugins from blocking the visibility state event
210210
if (this.game.stage.disableVisibilityChange === false && this.game.paused && this.game.scale.incorrectOrientation === false)
211211
{
212-
this.game.paused = false;
213-
return this;
212+
// this.game.paused = false;
213+
// return this;
214214
}
215215

216216
this._history.length = 0;

0 commit comments

Comments
 (0)