// gestures.js — 触控手势检测器(简化版) // // 仅支持 6 种手势:单击 · 双击 · 左滑 · 右滑 · 上滑 · 下滑 // 无长按,无多指操作 class GestureDetector { constructor(el) { this._el = el; this._handlers = {}; this._startX = 0; this._startY = 0; this._startTime = 0; this._tapTimer = null; this._tapCount = 0; this._bindTouch(); this._bindMouse(); this._bindKeyboard(); } on(evt, fn) { this._handlers[evt] = fn; return this; } _emit(evt) { dbg(evt); const fn = this._handlers[evt]; if (fn) fn(); } // ── 触摸事件 ── _bindTouch() { const el = this._el; el.addEventListener('touchstart', e => { e.preventDefault(); const t = e.touches[0]; this._startX = t.clientX; this._startY = t.clientY; this._startTime = Date.now(); }, { passive: false }); el.addEventListener('touchend', e => { e.preventDefault(); const t = e.changedTouches[0]; this._process(t.clientX, t.clientY); }, { passive: false }); el.addEventListener('touchcancel', () => this._reset(), { passive: false }); } // ── 鼠标事件(桌面调试)── _bindMouse() { this._el.addEventListener('mousedown', e => { this._startX = e.clientX; this._startY = e.clientY; this._startTime = Date.now(); }); this._el.addEventListener('mouseup', e => { this._process(e.clientX, e.clientY); }); } // ── 键盘事件(桌面调试)── _bindKeyboard() { document.addEventListener('keydown', e => { switch (e.code) { case 'Space': e.preventDefault(); this._emit('singletap'); break; case 'Enter': e.preventDefault(); this._emit('doubletap'); break; case 'ArrowLeft': e.preventDefault(); this._emit('swipe_left'); break; case 'ArrowRight': e.preventDefault(); this._emit('swipe_right'); break; case 'ArrowUp': e.preventDefault(); this._emit('swipe_up'); break; case 'ArrowDown': e.preventDefault(); this._emit('swipe_down'); break; } }); } // ── 手势判定 ── _process(endX, endY) { const dx = endX - this._startX; const dy = endY - this._startY; const dist = Math.sqrt(dx * dx + dy * dy); const dt = Date.now() - this._startTime; // 滑动:距离 > 35px if (dist > 35) { const angle = Math.abs(Math.atan2(dy, dx) * 180 / Math.PI); let dir = null; if (angle < 45) dir = 'right'; else if (angle > 135) dir = 'left'; else if (dy < 0 && Math.abs(dy) > Math.abs(dx)) dir = 'up'; else if (dy > 0 && Math.abs(dy) > Math.abs(dx)) dir = 'down'; if (dir) { this._tapCount = 0; clearTimeout(this._tapTimer); this._emit(`swipe_${dir}`); return; } } // 点击:距离 < 20px,时间 < 500ms if (dist < 20 && dt < 500) { this._tapCount++; if (this._tapCount === 1) { this._tapTimer = setTimeout(() => { this._tapCount = 0; this._emit('singletap'); }, 250); } else if (this._tapCount >= 2) { clearTimeout(this._tapTimer); this._tapCount = 0; this._emit('doubletap'); } } } _reset() { clearTimeout(this._tapTimer); this._tapCount = 0; } }