You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
6.9 KiB
175 lines
6.9 KiB
import { CommonEvent } from '../constants';
|
|
/**
|
|
* <zh/> 捏合手势处理器
|
|
*
|
|
* <en/> Pinch gesture handler
|
|
* @remarks
|
|
* <zh/> 处理双指触摸事件,计算缩放比例并触发回调。通过跟踪两个触摸点的位置变化,计算两点间距离变化率来确定缩放比例。
|
|
*
|
|
* <en/> Handles two-finger touch events, calculates zoom ratio and triggers callbacks. Tracks position changes of two touch points to determine zoom ratio based on distance variation.
|
|
*/
|
|
export class PinchHandler {
|
|
constructor(emitter, phase, callback) {
|
|
this.phase = phase;
|
|
/**
|
|
* <zh/> 当前跟踪的触摸点集合
|
|
*
|
|
* <en/> Currently tracked touch points collection
|
|
*/
|
|
this.pointerByTouch = [];
|
|
/**
|
|
* <zh/> 初始两点间距离
|
|
*
|
|
* <en/> Initial distance between two points
|
|
*/
|
|
this.initialDistance = null;
|
|
this.emitter = emitter;
|
|
if (PinchHandler.instance) {
|
|
PinchHandler.callbacks[this.phase].push(callback);
|
|
return PinchHandler.instance;
|
|
}
|
|
this.onPointerDown = this.onPointerDown.bind(this);
|
|
this.onPointerMove = this.onPointerMove.bind(this);
|
|
this.onPointerUp = this.onPointerUp.bind(this);
|
|
this.bindEvents();
|
|
PinchHandler.instance = this;
|
|
PinchHandler.callbacks[this.phase].push(callback);
|
|
}
|
|
bindEvents() {
|
|
const { emitter } = this;
|
|
emitter.on(CommonEvent.POINTER_DOWN, this.onPointerDown);
|
|
emitter.on(CommonEvent.POINTER_MOVE, this.onPointerMove);
|
|
emitter.on(CommonEvent.POINTER_UP, this.onPointerUp);
|
|
}
|
|
/**
|
|
* <zh/> 更新指定指针的位置
|
|
*
|
|
* <en/> Update position of specified pointer
|
|
* @param pointerId - <zh/> 指针唯一标识符 | <en/> Pointer unique identifier<sup>1</sup>
|
|
* @param x - <zh/> 新的X坐标 | <en/> New X coordinate
|
|
* @param y - <zh/> 新的Y坐标 | <en/> New Y coordinate
|
|
*/
|
|
updatePointerPosition(pointerId, x, y) {
|
|
const index = this.pointerByTouch.findIndex((p) => p.pointerId === pointerId);
|
|
if (index >= 0) {
|
|
this.pointerByTouch[index] = { x, y, pointerId };
|
|
}
|
|
}
|
|
/**
|
|
* <zh/> 处理指针按下事件
|
|
*
|
|
* <en/> Handle pointer down event
|
|
* @param event - <zh/> 指针事件对象 | <en/> Pointer event object
|
|
* @remarks
|
|
* <zh/> 当检测到两个触摸点时记录初始距离
|
|
*
|
|
* <en/> Record initial distance when detecting two touch points
|
|
*/
|
|
onPointerDown(event) {
|
|
const { x, y } = event.client || {};
|
|
if (x === undefined || y === undefined)
|
|
return;
|
|
this.pointerByTouch.push({ x, y, pointerId: event.pointerId });
|
|
if (event.pointerType === 'touch' && this.pointerByTouch.length === 2) {
|
|
PinchHandler.isPinching = true;
|
|
const dx = this.pointerByTouch[0].x - this.pointerByTouch[1].x;
|
|
const dy = this.pointerByTouch[0].y - this.pointerByTouch[1].y;
|
|
this.initialDistance = Math.sqrt(dx * dx + dy * dy);
|
|
PinchHandler.callbacks.pinchstart.forEach((cb) => cb(event, { scale: 0 }));
|
|
}
|
|
}
|
|
/**
|
|
* <zh/> 处理指针移动事件
|
|
*
|
|
* <en/> Handle pointer move event
|
|
* @param event - <zh/> 指针事件对象 | <en/> Pointer event object
|
|
* @remarks
|
|
* <zh/> 当存在两个有效触摸点时计算缩放比例
|
|
*
|
|
* <en/> Calculate zoom ratio when two valid touch points exist
|
|
*/
|
|
onPointerMove(event) {
|
|
if (this.pointerByTouch.length !== 2 || this.initialDistance === null)
|
|
return;
|
|
const { x, y } = event.client || {};
|
|
if (x === undefined || y === undefined)
|
|
return;
|
|
this.updatePointerPosition(event.pointerId, x, y);
|
|
const dx = this.pointerByTouch[0].x - this.pointerByTouch[1].x;
|
|
const dy = this.pointerByTouch[0].y - this.pointerByTouch[1].y;
|
|
const currentDistance = Math.sqrt(dx * dx + dy * dy);
|
|
const ratio = currentDistance / this.initialDistance;
|
|
PinchHandler.callbacks.pinchmove.forEach((cb) => cb(event, { scale: (ratio - 1) * 5 }));
|
|
}
|
|
/**
|
|
* <zh/> 处理指针抬起事件
|
|
*
|
|
* <en/> Handle pointer up event
|
|
* @param event
|
|
* @remarks
|
|
* <zh/> 重置触摸状态和初始距离
|
|
*
|
|
* <en/> Reset touch state and initial distance
|
|
*/
|
|
onPointerUp(event) {
|
|
var _a;
|
|
PinchHandler.callbacks.pinchend.forEach((cb) => cb(event, { scale: 0 }));
|
|
PinchHandler.isPinching = false;
|
|
this.initialDistance = null;
|
|
this.pointerByTouch = [];
|
|
(_a = PinchHandler.instance) === null || _a === void 0 ? void 0 : _a.tryDestroy();
|
|
}
|
|
/**
|
|
* <zh/> 销毁捏合手势相关监听
|
|
*
|
|
* <en/> Destroy pinch gesture listeners
|
|
* @remarks
|
|
* <zh/> 移除指针按下、移动、抬起事件的监听
|
|
*
|
|
* <en/> Remove listeners for pointer down, move, and up events
|
|
*/
|
|
destroy() {
|
|
this.emitter.off(CommonEvent.POINTER_DOWN, this.onPointerDown);
|
|
this.emitter.off(CommonEvent.POINTER_MOVE, this.onPointerMove);
|
|
this.emitter.off(CommonEvent.POINTER_UP, this.onPointerUp);
|
|
PinchHandler.instance = null;
|
|
}
|
|
/**
|
|
* <zh/> 解绑指定阶段的手势回调
|
|
* <en/> Unregister gesture callback for specific phase
|
|
* @param phase - <zh/> 手势阶段:开始(pinchstart)/移动(pinchmove)/结束(pinchend) | <en/> Gesture phase: start/move/end
|
|
* @param callback - <zh/> 要解绑的回调函数 | <en/> Callback function to unregister
|
|
* @remarks
|
|
* <zh/> 从指定阶段的回调列表中移除特定回调,当所有回调都解绑后自动销毁事件监听
|
|
* <en/> Remove specific callback from the phase's callback list, auto-destroy event listeners when all callbacks are unregistered
|
|
*/
|
|
off(phase, callback) {
|
|
const index = PinchHandler.callbacks[phase].indexOf(callback);
|
|
if (index > -1)
|
|
PinchHandler.callbacks[phase].splice(index, 1);
|
|
this.tryDestroy();
|
|
}
|
|
/**
|
|
* <zh/> 尝试销毁手势处理器
|
|
* <en/> Attempt to destroy the gesture handler
|
|
* @remarks
|
|
* <zh/> 当所有阶段(开始/移动/结束)的回调列表都为空时,执行实际销毁操作
|
|
* <en/> Perform actual destruction when all phase (pinchstart/pinchmove/pinchend) callback lists are empty
|
|
* <zh/> 自动解除事件监听并重置单例实例
|
|
* <en/> Automatically remove event listeners and reset singleton instance
|
|
*/
|
|
tryDestroy() {
|
|
if (Object.values(PinchHandler.callbacks).every((arr) => arr.length === 0)) {
|
|
this.destroy();
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* <zh/> 是否处于 Pinch 阶段
|
|
*
|
|
* <en/> Whether it is in the Pinch stage
|
|
*/
|
|
PinchHandler.isPinching = false;
|
|
PinchHandler.instance = null;
|
|
PinchHandler.callbacks = { pinchstart: [], pinchmove: [], pinchend: [] };
|
|
//# sourceMappingURL=pinch.js.map
|