Files
TIC-80/build/webapp/index.html
Carl Lange da3ea61dab [web export] Fix Safari Arrow keys in HTML export/webapp (#2838)
* web: Fix Safari Arrow keys in HTML export and webapp\n\n- Remap Arrow key events with KeyboardEvent.location === 3 (numpad) to location: 0 on WebKit in capture phase\n- Prevent page scrolling on Arrow keys while canvas is focused\n- Ensure canvas is focusable (tabindex=0) and focused on start\n\nThis addresses Safari/WebKit misclassification of physical Arrow keys so games receive directional input correctly.

* Add missing semicolon

* Update index.html

* Update index.html
2025-09-20 10:10:42 +02:00

142 lines
5.4 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#f4f4f4"/>
<link rel="manifest" href="tic80.webmanifest">
<link rel="apple-touch-icon" sizes="180x180" href="tic80-180.png">
<link rel="apple-touch-icon" sizes="192x192" href="tic80-192.png">
<link rel="apple-touch-icon" sizes="512x512" href="tic80-512.png">
<title>TIC-80</title>
<style>
body {
margin:0; padding:0; background: #000; display: flex; justify-content: center; align-items: center; height: 100vh;
}
.tic80-container {
width: 100%; padding-top: 56.66%; position: relative;
}
.tic80-canvas {
position: absolute; top: 0; left:0; width: 100%; height: 100%;
}
</style>
</head>
<body>
<div class="tic80-container">
<canvas class="tic80-canvas" id="canvas" oncontextmenu="event.preventDefault()" tabindex="1"></canvas>
</div>
<script type="module">
import startTic80 from './tic80.js'
navigator.serviceWorker.register('serviceworker.js');
// Safari/WebKit-only: remap Arrow keys with location 3 (numpad) to location 0.
(function(){
const ua = navigator.userAgent || '';
const isWebKit = /AppleWebKit/i.test(ua);
if (!isWebKit) return;
const synthesizeArrowWithLocation0 = (orig) => {
try {
const ev = new KeyboardEvent(orig.type, {
key: orig.key,
code: orig.code,
location: 0,
repeat: orig.repeat,
ctrlKey: orig.ctrlKey,
shiftKey: orig.shiftKey,
altKey: orig.altKey,
metaKey: orig.metaKey,
bubbles: true,
cancelable: true,
});
Object.defineProperty(ev, 'which', { get: () => orig.which });
Object.defineProperty(ev, 'keyCode', { get: () => orig.keyCode });
return ev;
} catch { return null; }
};
const remapArrowLocationCapture = (e) => {
const isArrow = (e.key && e.key.startsWith('Arrow')) || (e.code && e.code.startsWith('Arrow'));
if (!isArrow) return;
if (e.location === 3) {
const syn = synthesizeArrowWithLocation0(e);
if (syn) {
e.stopImmediatePropagation();
e.preventDefault();
(e.target || document.getElementById('canvas')).dispatchEvent(syn);
}
}
};
['keydown','keyup'].forEach(t => document.addEventListener(t, remapArrowLocationCapture, true));
})();
// Prevent page scrolling while the canvas is focused
(function(){
const canvasEl = document.getElementById('canvas');
const preventArrowDefault = (e) => {
const key = e.key || e.code;
const kc = e.keyCode || e.which;
const isArrow = (key && ['ArrowUp','ArrowDown','ArrowLeft','ArrowRight'].includes(key)) || (kc >= 37 && kc <= 40);
if (isArrow && document.activeElement === canvasEl) e.preventDefault();
};
document.addEventListener('keydown', preventArrowDefault, { passive: false });
canvasEl.addEventListener('keydown', preventArrowDefault, { passive: false });
})();
const initialize = () => {
const canvasSelector = '#canvas'
const canvasElement = document.querySelector(canvasSelector)
const options = {
canvas: document.getElementById('canvas'),
saveAs (blob, filename) {
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = filename
link.click()
},
showAddPopup (callback) {
callback(null, null)
var input = document.createElement('input')
input.type = 'file'
input.click()
input.addEventListener('change', (event) => {
const file = event.target.files[0]
if (file) {
var reader = new FileReader
reader.onload = function(event) {
var rom = new Uint8Array(event.target.result)
callback(file.name, rom)
}
reader.readAsArrayBuffer(file)
}
})
},
preRun: [
function (module) {
module.ENV.SDL_EMSCRIPTEN_KEYBOARD_ELEMENT = canvasSelector
}
]
}
canvasElement.focus()
startTic80(options)
}
window.addEventListener('DOMContentLoaded', initialize)
</script>
</body>
</html>