qFelix's picture
Brad Did Something - Gradio/FastAPI Space on HF
78c0f6e
Raw
History Blame Contribute Delete
3.06 kB
// touch.js — on-screen controls for phones/tablets (no physical keyboard).
//
// A left thumb-joystick drives the same `keys` object the player already reads,
// so movement reuses player.update untouched. Right-side ACTION and GIFT
// buttons fire the same handlers as SPACE and G. Created only on touch devices;
// CSS hides them whenever a menu/dialogue/comic owns the screen. Tap-to-move
// (bindStageClick) keeps working too.
import { keys, emit } from "./input.js";
const DEAD = 0.3; // joystick deadzone as a fraction of its radius
export function isTouch() {
return "ontouchstart" in window || navigator.maxTouchPoints > 0;
}
export function setupTouch() {
if (!isTouch()) return;
document.body.classList.add("touch");
buildJoystick();
buildButtons();
}
function clearKeys() {
keys.up = keys.down = keys.left = keys.right = false;
}
function buildJoystick() {
const base = document.createElement("div");
base.id = "tc-stick";
base.innerHTML = `<div class="tc-thumb"></div>`;
document.getElementById("viewport").appendChild(base);
const thumb = base.querySelector(".tc-thumb");
let id = null, cx = 0, cy = 0, r = 1;
function place(t) {
let dx = (t.clientX - cx) / r, dy = (t.clientY - cy) / r;
const mag = Math.hypot(dx, dy);
if (mag > 1) { dx /= mag; dy /= mag; }
thumb.style.transform = `translate(${dx * r * 0.55}px, ${dy * r * 0.55}px)`;
keys.left = dx < -DEAD; keys.right = dx > DEAD;
keys.up = dy < -DEAD; keys.down = dy > DEAD;
}
base.addEventListener("touchstart", (e) => {
const rect = base.getBoundingClientRect();
r = rect.width / 2; cx = rect.left + r; cy = rect.top + r;
id = e.changedTouches[0].identifier;
place(e.changedTouches[0]);
e.preventDefault();
}, { passive: false });
base.addEventListener("touchmove", (e) => {
for (const t of e.changedTouches)
if (t.identifier === id) { place(t); e.preventDefault(); }
}, { passive: false });
const end = (e) => {
for (const t of e.changedTouches)
if (t.identifier === id) {
id = null; clearKeys();
thumb.style.transform = "translate(0,0)";
}
};
base.addEventListener("touchend", end);
base.addEventListener("touchcancel", end);
}
function buildButtons() {
const wrap = document.createElement("div");
wrap.id = "tc-buttons";
wrap.innerHTML = `
<button class="tc-btn tc-gift" aria-label="gift">GIFT</button>
<button class="tc-btn tc-action" aria-label="action">ACT</button>`;
document.getElementById("viewport").appendChild(wrap);
const bind = (sel, name, arg) => {
const b = wrap.querySelector(sel);
// touchstart for zero-delay response; preventDefault stops the synth click
b.addEventListener("touchstart", (e) => {
e.preventDefault(); b.classList.add("down"); emit(name, arg);
}, { passive: false });
const up = () => b.classList.remove("down");
b.addEventListener("touchend", up);
b.addEventListener("touchcancel", up);
};
bind(".tc-action", "interact");
bind(".tc-gift", "gift");
}