| |
| |
| |
| |
| |
| |
| |
| import { keys, emit } from "./input.js"; |
|
|
| const DEAD = 0.3; |
|
|
| 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); |
| |
| 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"); |
| } |
|
|