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, initialDistance: 0,
initialZoom: 1, initialZoom: 1,
lastTouchTime: 0, 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会自动处理触摸转换 // 使用标准的mouse事件Fabric.js会自动处理触摸转换
this.canvas.on("mouse:down", (opt) => { this.canvas.on("mouse:down", (opt) => {
// 只在PAN模式下处理触摸事件 // 只在PAN模式下处理触摸事件
@@ -325,9 +350,21 @@ export class CanvasEventManager {
touches.length === 2 && touches.length === 2 &&
this.touchState.isZooming this.touchState.isZooming
) { ) {
// 双指缩放处理 // 双指缩放处理 - 修复抖动问题
const currentDistance = this.getTouchDistance(touches[0], touches[1]); const currentDistance = this.getTouchDistance(touches[0], touches[1]);
// 防止除零和异常值
if (this.touchState.initialDistance === 0 || currentDistance === 0) {
return;
}
const scale = currentDistance / this.touchState.initialDistance; const scale = currentDistance / this.touchState.initialDistance;
// 防止抖动:忽略微小的变化
if (Math.abs(scale - 1) < 0.01) {
return;
}
const newZoom = this.touchState.initialZoom * scale; const newZoom = this.touchState.initialZoom * scale;
// 限制缩放范围 // 限制缩放范围
@@ -424,7 +461,7 @@ export class CanvasEventManager {
} }
/** /**
* 设置原生触摸事件监听器(备用方案)- 优化性能 * 设置原生触摸事件监听器(备用方案)- 专门处理iPad双指缩放
*/ */
setupNativeTouchEvents() { setupNativeTouchEvents() {
const canvasElement = this.canvas.upperCanvasEl; const canvasElement = this.canvas.upperCanvasEl;
@@ -432,38 +469,126 @@ export class CanvasEventManager {
// 确保canvas元素支持触摸 // 确保canvas元素支持触摸
canvasElement.style.touchAction = "none"; canvasElement.style.touchAction = "none";
// 原生touchstart事件 - 简化处理 let lastTouchDistance = 0;
let lastZoom = 1;
// 原生touchstart事件 - 处理双指缩放初始化
canvasElement.addEventListener( canvasElement.addEventListener(
"touchstart", "touchstart",
(e) => { (e) => {
// 只在PAN模式下处理 if (this.editorMode !== OperationType.PAN) return;
if (this.editorMode === OperationType.PAN) {
// 调试信息
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(); e.preventDefault();
} }
}, },
{ passive: false } { passive: false }
); );
// 原生touchmove事件 - 简化处理 // 原生touchmove事件 - 处理双指缩放(修复抖动问题)
canvasElement.addEventListener( canvasElement.addEventListener(
"touchmove", "touchmove",
(e) => { (e) => {
// 只在PAN模式下处理 if (this.editorMode !== OperationType.PAN) return;
if (this.editorMode === OperationType.PAN) {
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(); e.preventDefault();
} }
}, },
{ passive: false } { passive: false }
); );
// 原生touchend事件 - 简化处理 // 原生touchend事件 - 重置缩放状态
canvasElement.addEventListener( canvasElement.addEventListener(
"touchend", "touchend",
(e) => { (e) => {
// 只在PAN模式下处理 if (this.editorMode !== OperationType.PAN) return;
if (this.editorMode === OperationType.PAN) {
e.preventDefault(); if (e.touches.length < 2) {
this.touchState.isZooming = false;
lastTouchDistance = 0;
} }
e.preventDefault();
}, },
{ passive: false } { passive: false }
); );