fix: 修复ipad双指缩放问题

This commit is contained in:
bighuixiang
2025-09-04 00:40:55 +08:00
parent 03f949f30f
commit 8ff64bdb7e

View File

@@ -250,8 +250,33 @@ export class CanvasEventManager {
initialDistance: 0,
initialZoom: 1,
lastTouchTime: 0,
lastZoomTime: 0,
zoomThrottle: 16, // 约60fps的节流
};
// iPad特殊处理禁用默认的触摸行为
if (this.deviceInfo.isTablet) {
document.addEventListener(
"touchstart",
(e) => {
if (e.target.closest("canvas")) {
e.preventDefault();
}
},
{ passive: false }
);
document.addEventListener(
"touchmove",
(e) => {
if (e.target.closest("canvas")) {
e.preventDefault();
}
},
{ passive: false }
);
}
// 使用标准的mouse事件Fabric.js会自动处理触摸转换
this.canvas.on("mouse:down", (opt) => {
// 只在PAN模式下处理触摸事件
@@ -325,9 +350,21 @@ export class CanvasEventManager {
touches.length === 2 &&
this.touchState.isZooming
) {
// 双指缩放处理
// 双指缩放处理 - 修复抖动问题
const currentDistance = this.getTouchDistance(touches[0], touches[1]);
// 防止除零和异常值
if (this.touchState.initialDistance === 0 || currentDistance === 0) {
return;
}
const scale = currentDistance / this.touchState.initialDistance;
// 防止抖动:忽略微小的变化
if (Math.abs(scale - 1) < 0.01) {
return;
}
const newZoom = this.touchState.initialZoom * scale;
// 限制缩放范围
@@ -424,7 +461,7 @@ export class CanvasEventManager {
}
/**
* 设置原生触摸事件监听器(备用方案)- 优化性能
* 设置原生触摸事件监听器(备用方案)- 专门处理iPad双指缩放
*/
setupNativeTouchEvents() {
const canvasElement = this.canvas.upperCanvasEl;
@@ -432,38 +469,126 @@ export class CanvasEventManager {
// 确保canvas元素支持触摸
canvasElement.style.touchAction = "none";
// 原生touchstart事件 - 简化处理
let lastTouchDistance = 0;
let lastZoom = 1;
// 原生touchstart事件 - 处理双指缩放初始化
canvasElement.addEventListener(
"touchstart",
(e) => {
// 只在PAN模式下处理
if (this.editorMode === OperationType.PAN) {
if (this.editorMode !== OperationType.PAN) return;
// 调试信息
if (process.env.NODE_ENV === "development") {
console.log("iPad touchstart:", e.touches.length, "fingers");
}
if (e.touches.length === 2) {
// 双指触摸开始
this.touchState.isZooming = true;
lastTouchDistance = this.getTouchDistance(e.touches[0], e.touches[1]);
lastZoom = this.canvas.getZoom();
// 计算缩放中心点
const centerX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
const centerY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
this.touchState.zoomCenter = { x: centerX, y: centerY };
if (process.env.NODE_ENV === "development") {
console.log("iPad双指缩放开始:", {
distance: lastTouchDistance,
zoom: lastZoom,
center: this.touchState.zoomCenter,
});
}
e.preventDefault();
}
},
{ passive: false }
);
// 原生touchmove事件 - 简化处理
// 原生touchmove事件 - 处理双指缩放(修复抖动问题)
canvasElement.addEventListener(
"touchmove",
(e) => {
// 只在PAN模式下处理
if (this.editorMode === OperationType.PAN) {
if (this.editorMode !== OperationType.PAN) return;
if (e.touches.length === 2 && this.touchState.isZooming) {
// 节流处理,避免过于频繁的缩放操作
const now = Date.now();
if (
now - this.touchState.lastZoomTime <
this.touchState.zoomThrottle
) {
return;
}
const currentDistance = this.getTouchDistance(
e.touches[0],
e.touches[1]
);
// 防止除零和异常值
if (lastTouchDistance === 0 || currentDistance === 0) {
return;
}
const scale = currentDistance / lastTouchDistance;
// 防止抖动:忽略微小的变化
if (Math.abs(scale - 1) < 0.02) {
return;
}
// 使用当前缩放值而不是初始缩放值,避免累积误差
const currentZoom = this.canvas.getZoom();
const newZoom = currentZoom * scale;
// 限制缩放范围
const clampedZoom = Math.max(0.1, Math.min(5, newZoom));
if (process.env.NODE_ENV === "development") {
console.log("iPad双指缩放中:", {
currentDistance,
lastTouchDistance,
scale,
currentZoom,
newZoom,
clampedZoom,
});
}
// 使用缩放中心点进行缩放
const point = new fabric.Point(
this.touchState.zoomCenter.x,
this.touchState.zoomCenter.y
);
this.canvas.zoomToPoint(point, clampedZoom);
// 更新基准距离和时间,避免累积误差
lastTouchDistance = currentDistance;
this.touchState.lastZoomTime = now;
e.preventDefault();
}
},
{ passive: false }
);
// 原生touchend事件 - 简化处理
// 原生touchend事件 - 重置缩放状态
canvasElement.addEventListener(
"touchend",
(e) => {
// 只在PAN模式下处理
if (this.editorMode === OperationType.PAN) {
e.preventDefault();
if (this.editorMode !== OperationType.PAN) return;
if (e.touches.length < 2) {
this.touchState.isZooming = false;
lastTouchDistance = 0;
}
e.preventDefault();
},
{ passive: false }
);