| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- // =====================================================
- // 类型定义
- // =====================================================
- interface GamepadConfig {
- DEBUG: boolean;
- AXIS_THRESHOLD: number;
- AXIS_COOLDOWN: number;
- DPAD_INDEX: {
- UP: number;
- DOWN: number;
- LEFT: number;
- RIGHT: number;
- };
- }
- interface DPadState {
- up: boolean;
- down: boolean;
- left: boolean;
- right: boolean;
- }
- interface GlobalState {
- lastAxisTime: number;
- lastAxisDirection: number;
- dpadPressed: DPadState;
- }
- // =====================================================
- // 配置区域
- // =====================================================
- const CONFIG: GamepadConfig = {
- DEBUG: true, // 日志总开关
- AXIS_THRESHOLD: 0.7, // 摇杆触发阈值
- AXIS_COOLDOWN: 200, // 摇杆触发冷却时间 (ms)
- DPAD_INDEX: { // Xbox 标准映射
- UP: 12,
- DOWN: 13,
- LEFT: 14,
- RIGHT: 15
- }
- };
- // =====================================================
- // 日志工具函数
- // =====================================================
- function log(...args: any[]): void {
- if (!CONFIG.DEBUG) return;
- console.log(...args);
- }
- // =====================================================
- // 状态记录
- // =====================================================
- const state: GlobalState = {
- lastAxisTime: 0,
- lastAxisDirection: 0,
- dpadPressed: {
- up: false,
- down: false,
- left: false,
- right: false
- }
- };
- // =====================================================
- // 通用工具函数
- // =====================================================
- /**
- * 平滑翻页函数
- * @param direction 1 = 向下, -1 = 向上
- */
- function scrollPage(direction: number): void {
- const offset = window.innerHeight - 80;
- const distance = direction === 1 ? offset : -offset;
- log(direction === 1 ? "🎮 翻页:向下" : "🎮 翻页:向上");
- window.scrollBy({
- top: distance,
- behavior: "smooth"
- });
- }
- /**
- * 章节切换函数
- * @param direction 1 = 下一章, -1 = 上一章
- */
- function switchChapter(direction: number): void {
- const buttons = document.querySelectorAll<HTMLElement>(".read-bar .tool-icon");
- if (buttons.length < 2) return;
- if (direction === 1) {
- log("🎮 切换章节:下一章");
- buttons[1].click();
- } else {
- log("🎮 切换章节:上一章");
- buttons[0].click();
- }
- }
- /**
- * 边沿检测函数
- */
- function isPressedOnce(current: boolean, previous: boolean): boolean {
- return current && !previous;
- }
- // =====================================================
- // 摇杆处理
- // =====================================================
- function handleAxis(gp: Gamepad, now: number): void {
- const axisY = gp.axes[1] || 0;
- if (Math.abs(axisY) <= CONFIG.AXIS_THRESHOLD) {
- state.lastAxisDirection = 0;
- return;
- }
- const direction = axisY > 0 ? 1 : -1;
- const cooldownPassed = now - state.lastAxisTime > CONFIG.AXIS_COOLDOWN;
- const directionChanged = direction !== state.lastAxisDirection;
- if (cooldownPassed || directionChanged) {
- state.lastAxisTime = now;
- state.lastAxisDirection = direction;
- log(`🎮 摇杆触发 | axisY=${axisY.toFixed(2)}`);
- scrollPage(direction);
- }
- }
- // =====================================================
- // DPad 处理
- // =====================================================
- function handleDPad(gp: Gamepad): void {
- const indexes = CONFIG.DPAD_INDEX;
- const current: DPadState = {
- up: gp.buttons[indexes.UP]?.pressed || false,
- down: gp.buttons[indexes.DOWN]?.pressed || false,
- left: gp.buttons[indexes.LEFT]?.pressed || false,
- right: gp.buttons[indexes.RIGHT]?.pressed || false
- };
- if (isPressedOnce(current.up, state.dpadPressed.up)) {
- log("🎮 DPad 上");
- scrollPage(-1);
- }
- if (isPressedOnce(current.down, state.dpadPressed.down)) {
- log("🎮 DPad 下");
- scrollPage(1);
- }
- if (isPressedOnce(current.left, state.dpadPressed.left)) {
- log("🎮 DPad 左");
- switchChapter(-1);
- }
- if (isPressedOnce(current.right, state.dpadPressed.right)) {
- log("🎮 DPad 右");
- switchChapter(1);
- }
- state.dpadPressed = current;
- }
- // =====================================================
- // 主手柄处理入口
- // =====================================================
- function handleGamepad(gp: Gamepad | null): void {
- if (!gp) return;
- const now = performance.now();
- handleAxis(gp, now);
- handleDPad(gp);
- }
- // =====================================================
- // 主循环
- // =====================================================
- let running = false;
- function gamepadLoop(): void {
- const gamepads = navigator.getGamepads?.() || [];
- for (const gp of gamepads) {
- if (gp) handleGamepad(gp);
- }
- requestAnimationFrame(gamepadLoop);
- }
- // =====================================================
- // 连接 / 断开 事件
- // =====================================================
- window.addEventListener("gamepadconnected", (e: GamepadEvent) => {
- log("🎮 手柄已连接:", e.gamepad.id);
- if (!running) {
- running = true;
- requestAnimationFrame(gamepadLoop);
- }
- });
- window.addEventListener("gamepaddisconnected", (e: GamepadEvent) => {
- log("🎮 手柄已断开:", e.gamepad.id);
- });
|