Files
kidgo/js/touch.js
T
2026-10-23 17:49:29 +08:00

99 lines
2.9 KiB
JavaScript

// 移动端虚拟手柄:左手方向、右手跳跃 + 道具(参考 IWBT Creator 的触屏布局)
// 左下方:← → 移动;右下方:大跳键 + 射击/下蹲 道具键 + 暂停/重生
import { setTouch, clearTouch } from './input.js';
import { Settings } from './settings.js';
// 持续按住型动作(通过 action 状态机驱动)
const HELD = ['left', 'right', 'jump', 'down'];
const api = {
pause() {},
suicide() {},
shoot() {},
};
let visible = false;
let root = null;
function q(sel) { return document.querySelector(sel); }
function bind(btn) {
const action = btn.dataset.touch;
if (!action) return;
let timer = null;
const press = (e) => {
e.preventDefault();
if (btn._pid != null) return; // 已在按住(多指防重入)
btn._pid = e.pointerId;
try { btn.setPointerCapture(e.pointerId); } catch (_) {}
btn.classList.add('on');
if (action === 'shoot') {
// 长按连发(受玩家冷却与是否持枪限制)
api.shoot();
timer = setInterval(() => api.shoot(), 140);
} else if (HELD.includes(action)) {
setTouch(action, true);
} else if (action === 'pause') {
api.pause();
} else if (action === 'suicide') {
api.suicide();
}
};
const release = (e) => {
if (btn._pid != null && e.pointerId !== btn._pid) return;
btn._pid = null;
try { btn.releasePointerCapture(e.pointerId); } catch (_) {}
btn.classList.remove('on');
if (action === 'shoot') {
if (timer) { clearInterval(timer); timer = null; }
} else if (HELD.includes(action)) {
setTouch(action, false);
}
};
btn.addEventListener('pointerdown', press);
btn.addEventListener('pointerup', release);
btn.addEventListener('pointercancel', release);
btn.addEventListener('lostpointercapture', release);
btn.addEventListener('contextmenu', (e) => e.preventDefault());
}
export const Touch = {
init(handlers) {
Object.assign(api, handlers || {});
root = q('#touch');
root.querySelectorAll('[data-touch]').forEach(bind);
// 游戏进行中禁止页面滚动/回弹
const stage = q('#stage');
if (stage) {
stage.addEventListener('touchmove', (e) => { if (visible) e.preventDefault(); }, { passive: false });
stage.addEventListener('contextmenu', (e) => { if (visible) e.preventDefault(); });
}
},
show() {
if (!root) root = q('#touch');
if (!root) return;
visible = true;
Settings.apply(); // 应用用户自定义的手柄布局
root.classList.remove('hidden');
},
hide() {
if (!root) root = q('#touch');
if (!root) return;
visible = false;
root.classList.add('hidden');
clearTouch(); // 防止按键卡住
},
/** 反映是否持有手枪:无枪时射击键置灰 */
setGun(has) {
const g = q('#tc-gun');
if (g) g.classList.toggle('off', !has);
},
};