Phase D/F/H: embedding shell - loading progress, fullscreen, file upload, touch controls
package/src/index.ts: fixes a real pre-existing bug (start()/loadTable() never actually called vpinball_wasm_start with a table path argument), adds byte-level download progress (onProgress) and requestFullscreen(). package/src/touch-controls.ts: new on-screen virtual flipper/plunger/start button overlay that synthesizes the actual default keyboard scancodes (SDL_SCANCODE_LSHIFT/RSHIFT/RETURN, read from InputManager.cpp) as real KeyboardEvents dispatched on window, matching SDL3's Emscripten keyboard target - keeps the native input path completely unmodified. examples/basic/index.html: a real demo page - progress bar, a user-gesture "Start" button (required for both audio autoplay and fullscreen), a file picker for self-contained .vpx uploads, and the touch overlay shown on touch-capable devices. scripts/build.sh: two real bugs found and fixed via actual browser testing: - MODULARIZE=1 without EXPORT_ES6=1 produces a classic script, not an ES module with a default export - `await import(...)).default` was always undefined. Fixes with -sEXPORT_ES6=1. - --closure 1 silently stripped FS.writeFile/readFile/mkdir down to just low-level node ops, breaking runtime table loading with no compile-time warning. Dropped until root-caused; -O2 alone is kept. Verified end-to-end in Chrome: full load->progress->start flow with no errors, fullscreen actually engaging (document.fullscreenElement true), and a file-picker-selected table loading correctly (LoadGameFromFilename /tables/uploaded.vpx in the boot log, followed by a normal render). README updated to reflect what's now validated vs. still open (real-device input/audio confirmation remains the main open item).
This commit is contained in:
@@ -1,26 +1,107 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
Minimal usage example for @valknar/vpinball-wasm.
|
||||
Full usage example for @valknar/vpinball-wasm: loading progress, a
|
||||
user-gesture "Start" button (required for audio autoplay and fullscreen
|
||||
to work), an optional file picker for a self-contained .vpx table, touch
|
||||
controls on touch-capable devices, and a fullscreen button.
|
||||
|
||||
Serve this directory alongside a built dist/ (see scripts/dev-server.sh)
|
||||
and adjust the import path below if dist/ isn't a sibling directory.
|
||||
and adjust the import paths below if dist/ isn't a sibling directory.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
||||
<title>vpinball-wasm example</title>
|
||||
<style>
|
||||
body { margin: 0; background: #111; }
|
||||
canvas { display: block; width: 100vw; height: 100vh; }
|
||||
html, body { margin: 0; height: 100%; background: #111; overflow: hidden; }
|
||||
#stage { position: relative; width: 100vw; height: 100vh; }
|
||||
canvas { display: block; width: 100%; height: 100%; padding: 0; border: 0; }
|
||||
|
||||
#overlay {
|
||||
position: absolute; inset: 0; display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center; gap: 16px;
|
||||
background: #111; color: #eee; font-family: sans-serif;
|
||||
}
|
||||
#overlay.hidden { display: none; }
|
||||
#progress-track { width: 280px; height: 8px; background: #333; border-radius: 4px; overflow: hidden; }
|
||||
#progress-bar { width: 0%; height: 100%; background: #4ade80; transition: width 0.1s linear; }
|
||||
#start-button {
|
||||
padding: 12px 32px; font-size: 18px; border-radius: 8px; border: none;
|
||||
background: #4ade80; color: #111; cursor: pointer;
|
||||
}
|
||||
#start-button:disabled { opacity: 0.4; cursor: default; }
|
||||
#file-picker { color: #aaa; font-size: 13px; }
|
||||
|
||||
#fullscreen-button {
|
||||
position: absolute; top: 12px; right: 12px; z-index: 10;
|
||||
padding: 8px 14px; border-radius: 6px; border: none;
|
||||
background: rgba(255,255,255,0.15); color: #eee; cursor: pointer;
|
||||
}
|
||||
#fullscreen-button.hidden { display: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas"></canvas>
|
||||
<div id="stage">
|
||||
<canvas id="canvas"></canvas>
|
||||
<div id="overlay">
|
||||
<div id="progress-track"><div id="progress-bar"></div></div>
|
||||
<div id="file-picker">
|
||||
<label>Optional: play your own table (self-contained .vpx only) <input type="file" id="table-file" accept=".vpx" /></label>
|
||||
</div>
|
||||
<button id="start-button" disabled>Loading...</button>
|
||||
</div>
|
||||
<button id="fullscreen-button" class="hidden">Fullscreen</button>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import { loadPinball } from '../../dist/index.js';
|
||||
import { loadPinball, attachTouchControls } from '../../dist/index.js';
|
||||
|
||||
const canvas = document.getElementById('canvas');
|
||||
const pinball = await loadPinball({ canvas, baseUrl: '../../dist' });
|
||||
pinball.start();
|
||||
const overlay = document.getElementById('overlay');
|
||||
const progressBar = document.getElementById('progress-bar');
|
||||
const startButton = document.getElementById('start-button');
|
||||
const fileInput = document.getElementById('table-file');
|
||||
const fullscreenButton = document.getElementById('fullscreen-button');
|
||||
|
||||
let uploadedTableData;
|
||||
fileInput.addEventListener('change', async () => {
|
||||
const file = fileInput.files?.[0];
|
||||
if (file) uploadedTableData = await file.arrayBuffer();
|
||||
});
|
||||
|
||||
const pinball = await loadPinball({
|
||||
canvas,
|
||||
baseUrl: '../../dist',
|
||||
onProgress: (fraction) => {
|
||||
progressBar.style.width = `${Math.round(fraction * 100)}%`;
|
||||
},
|
||||
});
|
||||
|
||||
startButton.disabled = false;
|
||||
startButton.textContent = 'Start';
|
||||
|
||||
startButton.addEventListener('click', () => {
|
||||
if (uploadedTableData) {
|
||||
pinball.loadTable(uploadedTableData);
|
||||
}
|
||||
pinball.start();
|
||||
overlay.classList.add('hidden');
|
||||
fullscreenButton.classList.remove('hidden');
|
||||
|
||||
// Touch controls are additive UI for touch-capable devices - not
|
||||
// required for desktop mouse+keyboard play.
|
||||
if ('ontouchstart' in window || navigator.maxTouchPoints > 0) {
|
||||
attachTouchControls({ container: document.getElementById('stage') });
|
||||
}
|
||||
});
|
||||
|
||||
fullscreenButton.addEventListener('click', () => {
|
||||
pinball.requestFullscreen().catch(() => {
|
||||
// Fullscreen can be rejected (e.g. already fullscreen, or the
|
||||
// browser denies it) - nothing to recover from here.
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user