@energy8platform/platform-core 0.17.1 → 0.18.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.
- package/README.md +50 -14
- package/dist/index.cjs.js +152 -34
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +12 -13
- package/dist/index.esm.js +151 -35
- package/dist/index.esm.js.map +1 -1
- package/dist/loading.cjs.js +152 -34
- package/dist/loading.cjs.js.map +1 -1
- package/dist/loading.d.ts +12 -13
- package/dist/loading.esm.js +151 -35
- package/dist/loading.esm.js.map +1 -1
- package/package.json +2 -1
- package/src/index.ts +2 -0
- package/src/loading/CSSPreloader.ts +180 -38
- package/src/loading/index.ts +6 -1
- package/src/types.ts +8 -2
package/dist/loading.esm.js
CHANGED
|
@@ -68,22 +68,23 @@ ${defs}
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
const PRELOADER_ID = '__ge-css-preloader__';
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
*/
|
|
71
|
+
const RECT_ID = 'ge-pl-loader-rect';
|
|
72
|
+
const TEXT_ID = 'ge-pl-loader-text';
|
|
73
|
+
const REMOVE_FADE_TIMEOUT_MS = 600;
|
|
75
74
|
const LOGO_SVG = buildLogoSVG({
|
|
76
75
|
idPrefix: 'pl',
|
|
77
76
|
svgClass: 'ge-logo-svg',
|
|
78
77
|
clipRectClass: 'ge-clip-rect',
|
|
78
|
+
clipRectId: RECT_ID,
|
|
79
79
|
textClass: 'ge-preloader-svg-text',
|
|
80
|
+
textId: TEXT_ID,
|
|
80
81
|
});
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
let state = null;
|
|
83
|
+
function clampProgress(p) {
|
|
84
|
+
if (!Number.isFinite(p))
|
|
85
|
+
return 0;
|
|
86
|
+
return Math.max(0, Math.min(1, p));
|
|
87
|
+
}
|
|
87
88
|
function createCSSPreloader(container, config) {
|
|
88
89
|
if (document.getElementById(PRELOADER_ID))
|
|
89
90
|
return;
|
|
@@ -94,15 +95,15 @@ function createCSSPreloader(container, config) {
|
|
|
94
95
|
: '#0a0a1a';
|
|
95
96
|
const bgGradient = config?.backgroundGradient ?? `linear-gradient(135deg, ${bgColor} 0%, #1a1a3e 100%)`;
|
|
96
97
|
const customHTML = config?.cssPreloaderHTML ?? '';
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
const overlay = document.createElement('div');
|
|
99
|
+
overlay.id = PRELOADER_ID;
|
|
100
|
+
overlay.innerHTML = customHTML || `
|
|
100
101
|
<div class="ge-preloader-content">
|
|
101
102
|
${LOGO_SVG}
|
|
102
103
|
</div>
|
|
103
104
|
`;
|
|
104
|
-
const
|
|
105
|
-
|
|
105
|
+
const styleEl = document.createElement('style');
|
|
106
|
+
styleEl.textContent = `
|
|
106
107
|
#${PRELOADER_ID} {
|
|
107
108
|
position: absolute;
|
|
108
109
|
top: 0; left: 0;
|
|
@@ -155,31 +156,146 @@ function createCSSPreloader(container, config) {
|
|
|
155
156
|
0%, 100% { opacity: 0.4; }
|
|
156
157
|
50% { opacity: 1; }
|
|
157
158
|
}
|
|
159
|
+
|
|
160
|
+
/* Stop shimmer once JS-driven progress takes over. */
|
|
161
|
+
.ge-clip-rect.driven {
|
|
162
|
+
animation: none;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* Tap-to-start CTA pulse. Compound selector outweighs the ambient
|
|
166
|
+
.ge-preloader-svg-text rule, swapping the animation cleanly. */
|
|
167
|
+
.ge-preloader-svg-text.ge-svg-pulse {
|
|
168
|
+
animation: ge-tap-pulse 1.2s ease-in-out infinite;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@keyframes ge-tap-pulse {
|
|
172
|
+
0%, 100% { opacity: 0.5; }
|
|
173
|
+
50% { opacity: 1; }
|
|
174
|
+
}
|
|
158
175
|
`;
|
|
159
176
|
container.style.position = container.style.position || 'relative';
|
|
160
|
-
container.appendChild(
|
|
161
|
-
container.appendChild(
|
|
177
|
+
container.appendChild(styleEl);
|
|
178
|
+
container.appendChild(overlay);
|
|
179
|
+
const rectEl = overlay.querySelector(`#${RECT_ID}`);
|
|
180
|
+
const textEl = overlay.querySelector(`#${TEXT_ID}`);
|
|
181
|
+
if (!rectEl || !textEl) {
|
|
182
|
+
// Custom HTML mode — no logo SVG, lifecycle API becomes mostly inert.
|
|
183
|
+
// We still record state so removeCSSPreloader works.
|
|
184
|
+
state = {
|
|
185
|
+
container,
|
|
186
|
+
overlay,
|
|
187
|
+
styleEl,
|
|
188
|
+
rectEl: null,
|
|
189
|
+
textEl: null,
|
|
190
|
+
showPercentage: false,
|
|
191
|
+
tapToStart: config?.tapToStart !== false,
|
|
192
|
+
tapToStartText: config?.tapToStartText ?? 'TAP TO START',
|
|
193
|
+
driven: false,
|
|
194
|
+
tapState: 'idle',
|
|
195
|
+
tapPromise: null,
|
|
196
|
+
tapResolve: null,
|
|
197
|
+
tapHandler: null,
|
|
198
|
+
removed: false,
|
|
199
|
+
};
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
state = {
|
|
203
|
+
container,
|
|
204
|
+
overlay,
|
|
205
|
+
styleEl,
|
|
206
|
+
rectEl,
|
|
207
|
+
textEl,
|
|
208
|
+
showPercentage: config?.showPercentage === true,
|
|
209
|
+
tapToStart: config?.tapToStart !== false,
|
|
210
|
+
tapToStartText: config?.tapToStartText ?? 'TAP TO START',
|
|
211
|
+
driven: false,
|
|
212
|
+
tapState: 'idle',
|
|
213
|
+
tapPromise: null,
|
|
214
|
+
tapResolve: null,
|
|
215
|
+
tapHandler: null,
|
|
216
|
+
removed: false,
|
|
217
|
+
};
|
|
162
218
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (!
|
|
219
|
+
function setCSSPreloaderProgress(progress) {
|
|
220
|
+
if (!state || state.removed)
|
|
221
|
+
return;
|
|
222
|
+
if (state.tapState === 'waiting' || state.tapState === 'resolved')
|
|
223
|
+
return;
|
|
224
|
+
if (!state.rectEl)
|
|
169
225
|
return;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
226
|
+
const p = clampProgress(progress);
|
|
227
|
+
if (!state.driven) {
|
|
228
|
+
state.rectEl.classList.add('driven');
|
|
229
|
+
state.driven = true;
|
|
230
|
+
}
|
|
231
|
+
state.rectEl.setAttribute('width', String(p * LOADER_BAR_MAX_WIDTH));
|
|
232
|
+
if (state.showPercentage && state.textEl) {
|
|
233
|
+
state.textEl.textContent = `${Math.round(p * 100)}%`;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function waitCSSPreloaderTap() {
|
|
237
|
+
if (!state) {
|
|
238
|
+
throw new Error('CSS preloader not initialized — call createCSSPreloader first');
|
|
239
|
+
}
|
|
240
|
+
if (state.removed)
|
|
241
|
+
return Promise.resolve();
|
|
242
|
+
if (!state.tapToStart)
|
|
243
|
+
return Promise.resolve();
|
|
244
|
+
if (state.tapPromise)
|
|
245
|
+
return state.tapPromise;
|
|
246
|
+
if (state.textEl) {
|
|
247
|
+
state.textEl.textContent = state.tapToStartText;
|
|
248
|
+
state.textEl.classList.add('ge-svg-pulse');
|
|
249
|
+
}
|
|
250
|
+
state.overlay.style.cursor = 'pointer';
|
|
251
|
+
state.tapState = 'waiting';
|
|
252
|
+
state.tapPromise = new Promise((resolve) => {
|
|
253
|
+
state.tapResolve = resolve;
|
|
254
|
+
const handler = (_e) => {
|
|
255
|
+
if (!state)
|
|
256
|
+
return;
|
|
257
|
+
state.overlay.removeEventListener('pointerdown', handler);
|
|
258
|
+
state.tapHandler = null;
|
|
259
|
+
state.tapState = 'resolved';
|
|
260
|
+
state.tapResolve = null;
|
|
261
|
+
resolve();
|
|
262
|
+
};
|
|
263
|
+
state.tapHandler = handler;
|
|
264
|
+
state.overlay.addEventListener('pointerdown', handler);
|
|
265
|
+
});
|
|
266
|
+
return state.tapPromise;
|
|
267
|
+
}
|
|
268
|
+
function removeCSSPreloader(_container) {
|
|
269
|
+
if (!state || state.removed)
|
|
270
|
+
return Promise.resolve();
|
|
271
|
+
// Detach the pending pointer listener (if any) and resolve a pending tap.
|
|
272
|
+
if (state.tapHandler) {
|
|
273
|
+
state.overlay.removeEventListener('pointerdown', state.tapHandler);
|
|
274
|
+
state.tapHandler = null;
|
|
275
|
+
}
|
|
276
|
+
if (state.tapState === 'waiting' && state.tapResolve) {
|
|
277
|
+
state.tapState = 'resolved';
|
|
278
|
+
state.tapResolve();
|
|
279
|
+
state.tapResolve = null;
|
|
280
|
+
}
|
|
281
|
+
state.removed = true;
|
|
282
|
+
const { overlay, styleEl } = state;
|
|
283
|
+
overlay.classList.add('ge-preloader-hidden');
|
|
284
|
+
return new Promise((resolve) => {
|
|
285
|
+
let settled = false;
|
|
286
|
+
const finish = () => {
|
|
287
|
+
if (settled)
|
|
288
|
+
return;
|
|
289
|
+
settled = true;
|
|
290
|
+
overlay.remove();
|
|
291
|
+
styleEl.remove();
|
|
292
|
+
state = null;
|
|
293
|
+
resolve();
|
|
294
|
+
};
|
|
295
|
+
overlay.addEventListener('transitionend', finish, { once: true });
|
|
296
|
+
setTimeout(finish, REMOVE_FADE_TIMEOUT_MS);
|
|
181
297
|
});
|
|
182
298
|
}
|
|
183
299
|
|
|
184
|
-
export { LOADER_BAR_MAX_WIDTH, buildLogoSVG, createCSSPreloader, removeCSSPreloader };
|
|
300
|
+
export { LOADER_BAR_MAX_WIDTH, buildLogoSVG, createCSSPreloader, removeCSSPreloader, setCSSPreloaderProgress, waitCSSPreloaderTap };
|
|
185
301
|
//# sourceMappingURL=loading.esm.js.map
|
package/dist/loading.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loading.esm.js","sources":["../src/loading/logo.ts","../src/loading/CSSPreloader.ts"],"sourcesContent":[null,null],"names":[],"mappings":"AAAA;;;;;;;AAOG;AAEH;AACA,MAAM,cAAc,GAAG;;;;;+XAKwW;AAE/X;AACA,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;sBAkBA;AAEtB;AACO,MAAM,oBAAoB,GAAG;AAqBpC;;;;;AAKG;AACG,SAAU,YAAY,CAAC,IAAoB,EAAA;AAC/C,IAAA,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,IAAI;;AAGxG,IAAA,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,UAAU,EAAE,CAAA,EAAG,QAAQ,CAAA,EAAA,CAAI,CAAC;AACjE,IAAA,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,UAAU,EAAE,CAAA,EAAG,QAAQ,CAAA,EAAA,CAAI,CAAC;AAE/D,IAAA,MAAM,MAAM,GAAG,CAAA,EAAG,QAAQ,cAAc;AACxC,IAAA,MAAM,cAAc,GAAG,CAAA,EAAG,QAAQ,GAAG;AAErC,IAAA,MAAM,SAAS,GAAG,QAAQ,GAAG,CAAA,QAAA,EAAW,QAAQ,CAAA,CAAA,CAAG,GAAG,EAAE;AACxD,IAAA,MAAM,SAAS,GAAG,QAAQ,GAAG,CAAA,QAAA,EAAW,QAAQ,CAAA,CAAA,CAAG,GAAG,EAAE;AACxD,IAAA,MAAM,aAAa,GAAG,aAAa,GAAG,CAAA,QAAA,EAAW,aAAa,CAAA,CAAA,CAAG,GAAG,EAAE;AACtE,IAAA,MAAM,UAAU,GAAG,UAAU,GAAG,CAAA,KAAA,EAAQ,UAAU,CAAA,CAAA,CAAG,GAAG,EAAE;AAC1D,IAAA,MAAM,SAAS,GAAG,MAAM,GAAG,CAAA,KAAA,EAAQ,MAAM,CAAA,CAAA,CAAG,GAAG,EAAE;AACjD,IAAA,MAAM,YAAY,GAAG,SAAS,GAAG,CAAA,QAAA,EAAW,SAAS,CAAA,CAAA,CAAG,GAAG,EAAE;IAE7D,OAAO,CAAA,yEAAA,EAA4E,SAAS,CAAA,EAAG,SAAS,CAAA;EACxG,KAAK;kBACW,MAAM,CAAA;AACb,SAAA,EAAA,UAAU,wCAAwC,aAAa,CAAA;;AAEkG,0KAAA,EAAA,cAAc,sBAAsB,MAAM,CAAA;AAC7M,OAAA,EAAA,SAAS,CAAA,mMAAA,EAAsM,YAAY,CAAA,CAAA,EAAI,WAAW,IAAI,YAAY,CAAA;;EAEjQ,IAAI;;OAEC;AACP;;AC3FA,MAAM,YAAY,GAAG,sBAAsB;
|
|
1
|
+
{"version":3,"file":"loading.esm.js","sources":["../src/loading/logo.ts","../src/loading/CSSPreloader.ts"],"sourcesContent":[null,null],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@energy8platform/platform-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Energy8 platform core: Lua engine, DevBridge, RTP simulation, and SDK session orchestration. Renderer-agnostic — pair with any game framework (Pixi, Phaser, Three.js, custom).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs.js",
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
"eslint": "^9.0.0",
|
|
78
78
|
"fengari": "^0.1.5",
|
|
79
79
|
"fengari-web": "^0.1.4",
|
|
80
|
+
"jsdom": "^25.0.1",
|
|
80
81
|
"rollup": "^4.24.0",
|
|
81
82
|
"rollup-plugin-dts": "^6.1.0",
|
|
82
83
|
"tslib": "^2.8.0",
|
package/src/index.ts
CHANGED
|
@@ -1,25 +1,44 @@
|
|
|
1
1
|
import type { LoadingScreenConfig } from '../types';
|
|
2
|
-
import { buildLogoSVG } from './logo';
|
|
2
|
+
import { buildLogoSVG, LOADER_BAR_MAX_WIDTH } from './logo';
|
|
3
3
|
|
|
4
4
|
const PRELOADER_ID = '__ge-css-preloader__';
|
|
5
|
+
const RECT_ID = 'ge-pl-loader-rect';
|
|
6
|
+
const TEXT_ID = 'ge-pl-loader-text';
|
|
7
|
+
const REMOVE_FADE_TIMEOUT_MS = 600;
|
|
5
8
|
|
|
6
|
-
/**
|
|
7
|
-
* Inline SVG logo with animated loader bar.
|
|
8
|
-
* The `#loader` path acts as the progress fill — animated via clipPath.
|
|
9
|
-
*/
|
|
10
9
|
const LOGO_SVG = buildLogoSVG({
|
|
11
10
|
idPrefix: 'pl',
|
|
12
11
|
svgClass: 'ge-logo-svg',
|
|
13
12
|
clipRectClass: 'ge-clip-rect',
|
|
13
|
+
clipRectId: RECT_ID,
|
|
14
14
|
textClass: 'ge-preloader-svg-text',
|
|
15
|
+
textId: TEXT_ID,
|
|
15
16
|
});
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
interface PreloaderState {
|
|
19
|
+
container: HTMLElement;
|
|
20
|
+
overlay: HTMLDivElement;
|
|
21
|
+
styleEl: HTMLStyleElement;
|
|
22
|
+
rectEl: SVGRectElement;
|
|
23
|
+
textEl: SVGTextElement;
|
|
24
|
+
showPercentage: boolean;
|
|
25
|
+
tapToStart: boolean;
|
|
26
|
+
tapToStartText: string;
|
|
27
|
+
driven: boolean;
|
|
28
|
+
tapState: 'idle' | 'waiting' | 'resolved';
|
|
29
|
+
tapPromise: Promise<void> | null;
|
|
30
|
+
tapResolve: (() => void) | null;
|
|
31
|
+
tapHandler: ((e: Event) => void) | null;
|
|
32
|
+
removed: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let state: PreloaderState | null = null;
|
|
36
|
+
|
|
37
|
+
function clampProgress(p: number): number {
|
|
38
|
+
if (!Number.isFinite(p)) return 0;
|
|
39
|
+
return Math.max(0, Math.min(1, p));
|
|
40
|
+
}
|
|
41
|
+
|
|
23
42
|
export function createCSSPreloader(
|
|
24
43
|
container: HTMLElement,
|
|
25
44
|
config?: LoadingScreenConfig,
|
|
@@ -33,20 +52,21 @@ export function createCSSPreloader(
|
|
|
33
52
|
? `#${config.backgroundColor.toString(16).padStart(6, '0')}`
|
|
34
53
|
: '#0a0a1a';
|
|
35
54
|
|
|
36
|
-
const bgGradient =
|
|
55
|
+
const bgGradient =
|
|
56
|
+
config?.backgroundGradient ?? `linear-gradient(135deg, ${bgColor} 0%, #1a1a3e 100%)`;
|
|
37
57
|
|
|
38
58
|
const customHTML = config?.cssPreloaderHTML ?? '';
|
|
39
59
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
60
|
+
const overlay = document.createElement('div');
|
|
61
|
+
overlay.id = PRELOADER_ID;
|
|
62
|
+
overlay.innerHTML = customHTML || `
|
|
43
63
|
<div class="ge-preloader-content">
|
|
44
64
|
${LOGO_SVG}
|
|
45
65
|
</div>
|
|
46
66
|
`;
|
|
47
67
|
|
|
48
|
-
const
|
|
49
|
-
|
|
68
|
+
const styleEl = document.createElement('style');
|
|
69
|
+
styleEl.textContent = `
|
|
50
70
|
#${PRELOADER_ID} {
|
|
51
71
|
position: absolute;
|
|
52
72
|
top: 0; left: 0;
|
|
@@ -99,31 +119,153 @@ export function createCSSPreloader(
|
|
|
99
119
|
0%, 100% { opacity: 0.4; }
|
|
100
120
|
50% { opacity: 1; }
|
|
101
121
|
}
|
|
122
|
+
|
|
123
|
+
/* Stop shimmer once JS-driven progress takes over. */
|
|
124
|
+
.ge-clip-rect.driven {
|
|
125
|
+
animation: none;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Tap-to-start CTA pulse. Compound selector outweighs the ambient
|
|
129
|
+
.ge-preloader-svg-text rule, swapping the animation cleanly. */
|
|
130
|
+
.ge-preloader-svg-text.ge-svg-pulse {
|
|
131
|
+
animation: ge-tap-pulse 1.2s ease-in-out infinite;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@keyframes ge-tap-pulse {
|
|
135
|
+
0%, 100% { opacity: 0.5; }
|
|
136
|
+
50% { opacity: 1; }
|
|
137
|
+
}
|
|
102
138
|
`;
|
|
103
139
|
|
|
104
140
|
container.style.position = container.style.position || 'relative';
|
|
105
|
-
container.appendChild(
|
|
106
|
-
container.appendChild(
|
|
141
|
+
container.appendChild(styleEl);
|
|
142
|
+
container.appendChild(overlay);
|
|
143
|
+
|
|
144
|
+
const rectEl = overlay.querySelector(`#${RECT_ID}`) as SVGRectElement | null;
|
|
145
|
+
const textEl = overlay.querySelector(`#${TEXT_ID}`) as SVGTextElement | null;
|
|
146
|
+
if (!rectEl || !textEl) {
|
|
147
|
+
// Custom HTML mode — no logo SVG, lifecycle API becomes mostly inert.
|
|
148
|
+
// We still record state so removeCSSPreloader works.
|
|
149
|
+
state = {
|
|
150
|
+
container,
|
|
151
|
+
overlay,
|
|
152
|
+
styleEl,
|
|
153
|
+
rectEl: null as unknown as SVGRectElement,
|
|
154
|
+
textEl: null as unknown as SVGTextElement,
|
|
155
|
+
showPercentage: false,
|
|
156
|
+
tapToStart: config?.tapToStart !== false,
|
|
157
|
+
tapToStartText: config?.tapToStartText ?? 'TAP TO START',
|
|
158
|
+
driven: false,
|
|
159
|
+
tapState: 'idle',
|
|
160
|
+
tapPromise: null,
|
|
161
|
+
tapResolve: null,
|
|
162
|
+
tapHandler: null,
|
|
163
|
+
removed: false,
|
|
164
|
+
};
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
state = {
|
|
169
|
+
container,
|
|
170
|
+
overlay,
|
|
171
|
+
styleEl,
|
|
172
|
+
rectEl,
|
|
173
|
+
textEl,
|
|
174
|
+
showPercentage: config?.showPercentage === true,
|
|
175
|
+
tapToStart: config?.tapToStart !== false,
|
|
176
|
+
tapToStartText: config?.tapToStartText ?? 'TAP TO START',
|
|
177
|
+
driven: false,
|
|
178
|
+
tapState: 'idle',
|
|
179
|
+
tapPromise: null,
|
|
180
|
+
tapResolve: null,
|
|
181
|
+
tapHandler: null,
|
|
182
|
+
removed: false,
|
|
183
|
+
};
|
|
107
184
|
}
|
|
108
185
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
186
|
+
export function setCSSPreloaderProgress(progress: number): void {
|
|
187
|
+
if (!state || state.removed) return;
|
|
188
|
+
if (state.tapState === 'waiting' || state.tapState === 'resolved') return;
|
|
189
|
+
if (!state.rectEl) return;
|
|
190
|
+
|
|
191
|
+
const p = clampProgress(progress);
|
|
192
|
+
|
|
193
|
+
if (!state.driven) {
|
|
194
|
+
state.rectEl.classList.add('driven');
|
|
195
|
+
state.driven = true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
state.rectEl.setAttribute('width', String(p * LOADER_BAR_MAX_WIDTH));
|
|
199
|
+
|
|
200
|
+
if (state.showPercentage && state.textEl) {
|
|
201
|
+
state.textEl.textContent = `${Math.round(p * 100)}%`;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function waitCSSPreloaderTap(): Promise<void> {
|
|
206
|
+
if (!state) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
'CSS preloader not initialized — call createCSSPreloader first',
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
if (state.removed) return Promise.resolve();
|
|
212
|
+
if (!state.tapToStart) return Promise.resolve();
|
|
213
|
+
if (state.tapPromise) return state.tapPromise;
|
|
214
|
+
|
|
215
|
+
if (state.textEl) {
|
|
216
|
+
state.textEl.textContent = state.tapToStartText;
|
|
217
|
+
state.textEl.classList.add('ge-svg-pulse');
|
|
218
|
+
}
|
|
219
|
+
state.overlay.style.cursor = 'pointer';
|
|
220
|
+
|
|
221
|
+
state.tapState = 'waiting';
|
|
222
|
+
state.tapPromise = new Promise<void>((resolve) => {
|
|
223
|
+
state!.tapResolve = resolve;
|
|
224
|
+
const handler = (_e: Event) => {
|
|
225
|
+
if (!state) return;
|
|
226
|
+
state.overlay.removeEventListener('pointerdown', handler);
|
|
227
|
+
state.tapHandler = null;
|
|
228
|
+
state.tapState = 'resolved';
|
|
229
|
+
state.tapResolve = null;
|
|
230
|
+
resolve();
|
|
231
|
+
};
|
|
232
|
+
state!.tapHandler = handler;
|
|
233
|
+
state!.overlay.addEventListener('pointerdown', handler);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
return state.tapPromise;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function removeCSSPreloader(_container: HTMLElement): Promise<void> {
|
|
240
|
+
if (!state || state.removed) return Promise.resolve();
|
|
241
|
+
|
|
242
|
+
// Detach the pending pointer listener (if any) and resolve a pending tap.
|
|
243
|
+
if (state.tapHandler) {
|
|
244
|
+
state.overlay.removeEventListener('pointerdown', state.tapHandler);
|
|
245
|
+
state.tapHandler = null;
|
|
246
|
+
}
|
|
247
|
+
if (state.tapState === 'waiting' && state.tapResolve) {
|
|
248
|
+
state.tapState = 'resolved';
|
|
249
|
+
state.tapResolve();
|
|
250
|
+
state.tapResolve = null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
state.removed = true;
|
|
254
|
+
const { overlay, styleEl } = state;
|
|
255
|
+
overlay.classList.add('ge-preloader-hidden');
|
|
256
|
+
|
|
257
|
+
return new Promise<void>((resolve) => {
|
|
258
|
+
let settled = false;
|
|
259
|
+
const finish = () => {
|
|
260
|
+
if (settled) return;
|
|
261
|
+
settled = true;
|
|
262
|
+
overlay.remove();
|
|
263
|
+
styleEl.remove();
|
|
264
|
+
state = null;
|
|
265
|
+
resolve();
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
overlay.addEventListener('transitionend', finish, { once: true });
|
|
269
|
+
setTimeout(finish, REMOVE_FADE_TIMEOUT_MS);
|
|
128
270
|
});
|
|
129
271
|
}
|
package/src/loading/index.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {
|
|
2
|
+
createCSSPreloader,
|
|
3
|
+
setCSSPreloaderProgress,
|
|
4
|
+
waitCSSPreloaderTap,
|
|
5
|
+
removeCSSPreloader,
|
|
6
|
+
} from './CSSPreloader';
|
|
2
7
|
export { buildLogoSVG, LOADER_BAR_MAX_WIDTH } from './logo';
|
|
3
8
|
export type { LoadingScreenConfig, AssetManifest, AssetBundle, AssetEntry } from '../types';
|
package/src/types.ts
CHANGED
|
@@ -55,9 +55,15 @@ export interface LoadingScreenConfig {
|
|
|
55
55
|
showPercentage?: boolean;
|
|
56
56
|
/** Custom progress text formatter */
|
|
57
57
|
progressTextFormatter?: (progress: number) => string;
|
|
58
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* If true (default), `waitCSSPreloaderTap()` blocks until the user
|
|
60
|
+
* clicks the preloader. Set to `false` to make `waitCSSPreloaderTap()`
|
|
61
|
+
* resolve immediately (skip-flag for games that don't want a manual
|
|
62
|
+
* gate). Useful for mobile audio unlock — the click satisfies the
|
|
63
|
+
* browser's user-gesture requirement.
|
|
64
|
+
*/
|
|
59
65
|
tapToStart?: boolean;
|
|
60
|
-
/**
|
|
66
|
+
/** Label shown in the SVG text element while waiting for tap. Default: 'TAP TO START'. */
|
|
61
67
|
tapToStartText?: string;
|
|
62
68
|
/** Minimum display time in ms (so the user sees the brand, even if loading is fast) */
|
|
63
69
|
minDisplayTime?: number;
|