239 lines
6.8 KiB
JavaScript
239 lines
6.8 KiB
JavaScript
class GestureHandler {
|
|
constructor(context, transformMatrix, { container }) {
|
|
this.transformMatrix = transformMatrix;
|
|
this.containerRect = container;
|
|
this.panThreshold = 5;
|
|
this.panStarted = false;
|
|
|
|
// 判断是否是小程序环境
|
|
this.isMiniProgram = typeof wx !== 'undefined' || typeof uni !== 'undefined';
|
|
|
|
// 小程序环境下直接使用降级方案
|
|
if (this.isMiniProgram) {
|
|
console.log('小程序环境,使用降级手势处理器');
|
|
this.catchEvent = this.createSimpleGestureHandler();
|
|
return;
|
|
}
|
|
|
|
// 非小程序环境尝试使用AnyTouch
|
|
try {
|
|
const atOptions = {
|
|
getPoint: touch => ({
|
|
x: touch.clientX - this.containerRect.left,
|
|
y: touch.clientY - this.containerRect.top
|
|
}),
|
|
preventDefault: false
|
|
};
|
|
|
|
// 提供兼容小程序的虚拟元素
|
|
atOptions.element = {
|
|
style: {},
|
|
addEventListener: () => {},
|
|
removeEventListener: () => {},
|
|
ownerDocument: {
|
|
documentElement: {
|
|
style: {}
|
|
}
|
|
}
|
|
};
|
|
|
|
// 动态导入AnyTouch避免小程序环境问题
|
|
import('any-touch').then(AnyTouch => {
|
|
this.at = new AnyTouch.default(atOptions);
|
|
this.setupGestures();
|
|
console.log('AnyTouch手势处理器已初始化');
|
|
}).catch(e => {
|
|
console.error('AnyTouch加载失败:', e);
|
|
this.catchEvent = this.createSimpleGestureHandler();
|
|
});
|
|
} catch (e) {
|
|
console.error('AnyTouch初始化失败:', e);
|
|
this.catchEvent = this.createSimpleGestureHandler();
|
|
}
|
|
}
|
|
|
|
// 创建小程序专用的简化手势处理器
|
|
createSimpleGestureHandler() {
|
|
let isClick = true;
|
|
let startPoint = null;
|
|
let startTime = null;
|
|
let lastPoint = null;
|
|
|
|
return (event) => {
|
|
const touches = event.touches || [];
|
|
|
|
if (touches.length === 1) {
|
|
const getPoint = (t) => ({
|
|
x: t.x || (t.clientX - this.containerRect.left),
|
|
y: t.y || (t.clientY - this.containerRect.top)
|
|
});
|
|
|
|
const currentPoint = getPoint(touches[0]);
|
|
|
|
// 第一次触摸
|
|
if (!startPoint) {
|
|
startPoint = currentPoint;
|
|
startTime = Date.now();
|
|
}
|
|
|
|
// 计算移动距离
|
|
const dx = currentPoint.x - startPoint.x;
|
|
const dy = currentPoint.y - startPoint.y;
|
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
|
|
// 如果是点击(移动距离小于阈值且时间短)
|
|
if (isClick && distance < 5 && Date.now() - startTime < 200) {
|
|
return; // 不执行移动操作
|
|
}
|
|
|
|
// 标记为非点击操作
|
|
isClick = false;
|
|
|
|
// 执行移动操作
|
|
if (lastPoint) {
|
|
const moveDx = currentPoint.x - lastPoint.x;
|
|
const moveDy = currentPoint.y - lastPoint.y;
|
|
|
|
this.transformMatrix.tx += moveDx;
|
|
this.transformMatrix.ty += moveDy;
|
|
}
|
|
|
|
lastPoint = currentPoint;
|
|
} else if (touches.length > 1) {
|
|
this._handlePinch(touches);
|
|
}
|
|
|
|
// 在触摸结束时重置状态
|
|
if (event.type === 'touchend' || event.type === 'touchcancel') {
|
|
isClick = true;
|
|
startPoint = null;
|
|
startTime = null;
|
|
lastPoint = null;
|
|
}
|
|
};
|
|
}
|
|
|
|
setupGestures() {
|
|
// 平移手势
|
|
this.at.on('pan', (event) => {
|
|
if (event.type === 'panstart') {
|
|
this.panStarted = false;
|
|
}
|
|
else if (event.type === 'panmove') {
|
|
const distance = Math.sqrt(event.deltaX**2 + event.deltaY**2);
|
|
|
|
if (!this.panStarted && distance < this.panThreshold) return;
|
|
|
|
if (!this.panStarted) {
|
|
this.panStarted = true;
|
|
}
|
|
|
|
const currentScale = Math.sqrt(
|
|
this.transformMatrix.a * this.transformMatrix.a +
|
|
this.transformMatrix.b * this.transformMatrix.b
|
|
);
|
|
|
|
const dx = event.deltaX / currentScale;
|
|
const dy = event.deltaY / currentScale;
|
|
|
|
this.transformMatrix.translate(dx, dy);
|
|
} else if (event.type === 'panend') {
|
|
this.panStarted = false;
|
|
}
|
|
});
|
|
|
|
// 缩放手势
|
|
this.at.on('pinch', (event) => {
|
|
if (event.type === 'pinchstart') {
|
|
this.lastScale = 1;
|
|
}
|
|
else if (event.type === 'pinchmove') {
|
|
const scaleChange = event.scale / this.lastScale;
|
|
this.lastScale = event.scale;
|
|
|
|
const centerX = event.center.x;
|
|
const centerY = event.center.y;
|
|
|
|
this.transformMatrix.scale(scaleChange, scaleChange, centerX, centerY);
|
|
}
|
|
});
|
|
}
|
|
|
|
// 公共接口
|
|
catchEvent(event) {
|
|
// 当触摸点数量变化时终止当前手势
|
|
if (this.lastTouchCount !== event.touches.length) {
|
|
this.panStarted = false;
|
|
this.lastPoints = null;
|
|
}
|
|
this.lastTouchCount = event.touches.length;
|
|
|
|
try {
|
|
if (this.at) {
|
|
this.at.run(event);
|
|
} else if (this.catchEvent) {
|
|
this.catchEvent(event);
|
|
}
|
|
} catch (e) {
|
|
console.error('手势处理错误:', e);
|
|
}
|
|
}
|
|
|
|
// 基础平移手势处理
|
|
_handlePan(touch) {
|
|
const getPoint = (t) => ({
|
|
x: t.x || (t.clientX - this.containerRect.left),
|
|
y: t.y || (t.clientY - this.containerRect.top)
|
|
});
|
|
|
|
const currentPoint = getPoint(touch);
|
|
|
|
if (this.lastPoint) {
|
|
const dx = currentPoint.x - this.lastPoint.x;
|
|
const dy = currentPoint.y - this.lastPoint.y;
|
|
|
|
this.transformMatrix.tx += dx;
|
|
this.transformMatrix.ty += dy;
|
|
}
|
|
|
|
this.lastPoint = currentPoint;
|
|
}
|
|
|
|
// 基础缩放手势处理
|
|
_handlePinch(touches) {
|
|
const point1 = touches[0];
|
|
const point2 = touches[1];
|
|
|
|
const getPoint = (touch) => ({
|
|
x: touch.x || (touch.clientX - this.containerRect.left),
|
|
y: touch.y || (t.clientY - this.containerRect.top)
|
|
});
|
|
|
|
const currentPoints = [getPoint(point1), getPoint(point2)];
|
|
|
|
if (this.lastPoints) {
|
|
const currentDistance = Math.sqrt(
|
|
Math.pow(currentPoints[1].x - currentPoints[0].x, 2) +
|
|
Math.pow(currentPoints[1].y - currentPoints[0].y, 2)
|
|
);
|
|
|
|
const prevDistance = Math.sqrt(
|
|
Math.pow(this.lastPoints[1].x - this.lastPoints[0].x, 2) +
|
|
Math.pow(this.lastPoints[1].y - this.lastPoints[0].y, 2)
|
|
);
|
|
|
|
if (prevDistance > 0 && currentDistance > 0) {
|
|
const scaleChange = currentDistance / prevDistance;
|
|
|
|
const centerX = (currentPoints[0].x + currentPoints[1].x) / 2;
|
|
const centerY = (currentPoints[0].y + currentPoints[1].y) / 2;
|
|
|
|
this.transformMatrix.scale(scaleChange, scaleChange, centerX, centerY);
|
|
}
|
|
}
|
|
|
|
this.lastPoints = currentPoints;
|
|
}
|
|
}
|
|
|
|
export default GestureHandler; |