@vpmedia/phaser 1.125.0 → 1.127.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,142 @@
1
+ /**
2
+ * A minimal page lifecycle observer.
3
+ * @see https://developer.chrome.com/docs/web-platform/page-lifecycle-api
4
+ */
5
+
6
+ /** The page is visible and has focus. */
7
+ export const PAGE_STATE_ACTIVE = 'active';
8
+ /** The page is visible but does not have focus. */
9
+ export const PAGE_STATE_PASSIVE = 'passive';
10
+ /** The page is not visible. */
11
+ export const PAGE_STATE_HIDDEN = 'hidden';
12
+ /** The browser has frozen the page to reclaim its resources. */
13
+ export const PAGE_STATE_FROZEN = 'frozen';
14
+ /** The page is being unloaded. */
15
+ export const PAGE_STATE_TERMINATED = 'terminated';
16
+
17
+ /** One of the states a page can be in. */
18
+ export type PageState =
19
+ | typeof PAGE_STATE_ACTIVE
20
+ | typeof PAGE_STATE_PASSIVE
21
+ | typeof PAGE_STATE_HIDDEN
22
+ | typeof PAGE_STATE_FROZEN
23
+ | typeof PAGE_STATE_TERMINATED;
24
+
25
+ /** Called with the state the page has just entered and the one it left. */
26
+ export type PageStateListener = (state: PageState, previous: PageState) => void;
27
+
28
+ const listeners = new Set<PageStateListener>();
29
+ let currentState: PageState | null = null;
30
+ let started = false;
31
+
32
+ /**
33
+ * Reads the state the page is in right now.
34
+ * @returns {PageState} The current page state.
35
+ */
36
+ export const readPageState = (): PageState => {
37
+ if (document.visibilityState === 'hidden') {
38
+ return PAGE_STATE_HIDDEN;
39
+ }
40
+ return document.hasFocus() ? PAGE_STATE_ACTIVE : PAGE_STATE_PASSIVE;
41
+ };
42
+
43
+ /**
44
+ * Records a new state and tells the listeners, ignoring transitions to the state already held.
45
+ * @param {PageState} next - The state the page has entered.
46
+ */
47
+ const enterState = (next: PageState): void => {
48
+ const previous = currentState ?? readPageState();
49
+ if (next === previous) {
50
+ return;
51
+ }
52
+ currentState = next;
53
+ const current = [...listeners];
54
+ for (const listener of current) {
55
+ listener(next, previous);
56
+ }
57
+ };
58
+
59
+ /**
60
+ * Starts observing the page, if it is not observed already.
61
+ */
62
+ export const startPageLifecycle = (): void => {
63
+ if (started) {
64
+ return;
65
+ }
66
+ started = true;
67
+ currentState = readPageState();
68
+ for (const type of ['pageshow', 'focus', 'blur', 'visibilitychange', 'resume']) {
69
+ globalThis.addEventListener(
70
+ type,
71
+ (event: Event): void => {
72
+ // blur and focus also fire for elements inside the page, which say nothing about the page
73
+ const fromElement = event.target !== globalThis && event.target !== document;
74
+ if ((event.type === 'blur' || event.type === 'focus') && fromElement) {
75
+ return;
76
+ }
77
+ enterState(readPageState());
78
+ },
79
+ true
80
+ );
81
+ }
82
+ globalThis.addEventListener(
83
+ 'freeze',
84
+ (): void => {
85
+ enterState(PAGE_STATE_FROZEN);
86
+ },
87
+ true
88
+ );
89
+ globalThis.addEventListener(
90
+ 'pagehide',
91
+ (event: Event): void => {
92
+ enterState((event as PageTransitionEvent).persisted ? PAGE_STATE_FROZEN : PAGE_STATE_TERMINATED);
93
+ },
94
+ true
95
+ );
96
+ };
97
+
98
+ /**
99
+ * Returns the state the page is in, starting the observer on first use.
100
+ * @returns {PageState} The current page state.
101
+ */
102
+ export const getPageState = (): PageState => {
103
+ startPageLifecycle();
104
+ return currentState ?? readPageState();
105
+ };
106
+
107
+ /**
108
+ * Registers a listener for every state change.
109
+ * @param {PageStateListener} listener - The listener to call on each change.
110
+ * @returns {() => void} A function that removes the listener again.
111
+ */
112
+ export const onPageStateChange = (listener: PageStateListener): (() => void) => {
113
+ startPageLifecycle();
114
+ listeners.add(listener);
115
+ return (): void => {
116
+ listeners.delete(listener);
117
+ };
118
+ };
119
+
120
+ /**
121
+ * Runs a callback the next time the page becomes active, once.
122
+ * @param {() => void} callback - The callback to run.
123
+ * @returns {() => void} A function that cancels the pending callback.
124
+ */
125
+ export const oncePageActive = (callback: () => void): (() => void) => {
126
+ const stop = onPageStateChange((state: PageState): void => {
127
+ if (state === PAGE_STATE_ACTIVE) {
128
+ stop();
129
+ callback();
130
+ }
131
+ });
132
+ return stop;
133
+ };
134
+
135
+ /**
136
+ * Drops every listener and stops tracking. Only the tests need this.
137
+ */
138
+ export const resetPageLifecycle = (): void => {
139
+ listeners.clear();
140
+ currentState = null;
141
+ started = false;
142
+ };
@@ -453,7 +453,7 @@ export class ScaleManager {
453
453
  }
454
454
  this.bounds.setTo(this.offset.x, this.offset.y, this.width, this.height);
455
455
  // Can be invoked in boot pre-input
456
- if (this.game.input as Input | undefined) {
456
+ if ((this.game.input as Input | undefined)?.scale) {
457
457
  this.game.input.scale.setTo(this.scaleFactor.x, this.scaleFactor.y);
458
458
  }
459
459
  }
@@ -1,10 +1,4 @@
1
1
  import type { Game } from './game.js';
2
- import {
3
- addPageLifecycleCallback,
4
- getPageLifecycleEventEmitter,
5
- PAGE_LIFECYCLE_STATE_ACTIVE,
6
- PAGE_LIFECYCLE_STATE_CHANGE_EVENT,
7
- } from '@vpmedia/simplify';
8
2
  import { ArraySet } from './array_set.js';
9
3
  import { AUDIO_DISABLED, AUDIO_STANDARD, AUDIO_WEBKIT } from './const.js';
10
4
  import { ENGINE_ERROR_CREATING_AUDIO_CONTEXT } from './error_code.js';
@@ -12,6 +6,7 @@ import { Signal } from './signal.js';
12
6
  import { Sound } from './sound.js';
13
7
  import { SoundSprite } from './sound_sprite.js';
14
8
  import type { Callback } from './callback.js';
9
+ import { onPageStateChange, oncePageActive } from './page_lifecycle.js';
15
10
 
16
11
  export class SoundManager {
17
12
  public game!: Game;
@@ -115,7 +110,7 @@ export class SoundManager {
115
110
  this.addUnlockHandlers();
116
111
  }
117
112
  this.context.addEventListener('statechange', this.onContextStateChange, false);
118
- getPageLifecycleEventEmitter().on(PAGE_LIFECYCLE_STATE_CHANGE_EVENT, this.onPageLifecycleChange);
113
+ onPageStateChange(this.onPageLifecycleChange);
119
114
  }
120
115
 
121
116
  /**
@@ -293,7 +288,7 @@ export class SoundManager {
293
288
  const typedError = error instanceof Error ? error : new Error(String(error));
294
289
  this.game.logger.fatal('SoundManager', { error: typedError, tags: { 'asset.key': key } });
295
290
  if (typedError.name === 'InvalidStateError') {
296
- addPageLifecycleCallback(PAGE_LIFECYCLE_STATE_ACTIVE, (): void => {
291
+ oncePageActive((): void => {
297
292
  void this.decode(key);
298
293
  });
299
294
  } else if (typedError.name === 'EncodingError') {
@@ -470,8 +470,8 @@ export const buildPoly = (graphicsData: DisplayGraphicsData, webGLData: Graphics
470
470
  const r = color[0] * alpha;
471
471
  const g = color[1] * alpha;
472
472
  const b = color[2] * alpha;
473
- const triangles = triangulate(points, null, 2);
474
- if (triangles.length === 0) {
473
+ const triangles: number[] | null = triangulate(points, null, 2);
474
+ if (!triangles?.length) {
475
475
  return false;
476
476
  }
477
477
  const vertPos = verts.length / 6;