Files
TIC-80/build/html/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

117 lines
5.5 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>TIC-80 tiny computer</title>
<style type="text/css">
.modal{display:none;position:fixed;z-index:1;padding-top:100px;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:#000;background-color:rgba(0,0,0,.4)}
.modal-content{color: #333c57;position:relative;background-color:#fefefe;margin:auto;padding:2px 16px;border:1px solid #888;width:500px;box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);-webkit-animation-name:animatetop;-webkit-animation-duration:.4s;animation-name:animatetop;animation-duration:.4s}@keyframes animatetop{from{top:-300px;opacity:0}to{top:0;opacity:1}}
.close{color:#000;float:right;font-size:28px;font-weight:700}
.close:focus,
.close:hover{color:#000;text-decoration:none;cursor:pointer}
#game-frame > div { font-size: 44px; font-family: monospace; font-weight: bold;}
/* You can set custom dimensions here */
.game { width: 100vw; height: 100vh; }
</style>
</head>
<body style="margin:0; padding:0;">
<div class="game" style="margin: 0; position: relative; background: #1a1c2c;">
<div id="game-frame" style="cursor: pointer; position: absolute; margin: 0 auto; opacity: 1; background: #1a1c2c; width: 100%; height: 100%;">
<div style="text-align: center; color: white; display: flex; justify-content: center; align-items: center; width: 100%; height: 100%;">
<p style="margin: 0;">- CLICK TO PLAY -</p>
</div>
</div>
<canvas style="width: 100%; height: 100%; margin: 0 auto; display: block; image-rendering: pixelated;" id="canvas" oncontextmenu="event.preventDefault()" onmousedown="window.focus()" tabindex="0"></canvas>
</div>
<div id="add-modal" class="modal">
<div class="modal-content">
<span class="close">&times</span>
<p>Select a file to add to the computer</p>
<p><input type="file" id="upload-input"></input></p>
</div>
</div>
<script type="text/javascript">
var Module = {canvas: document.getElementById('canvas')};
// 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 game 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 gameFrame = document.getElementById('game-frame')
gameFrame.addEventListener('click', function() {
let scriptTag = document.createElement('script'), // create a script tag
firstScriptTag = document.getElementsByTagName('script')[0]; // find the first script tag in the document
scriptTag.src = 'tic80.js'; // set the source of the script to your script
firstScriptTag.parentNode.insertBefore(scriptTag, firstScriptTag); // append the script to the DOM
this.remove()
// Focus canvas to ensure keyboard events are routed to the game
document.getElementById('canvas').focus()
});
</script>
</body>
</html>