fix: 修复ipad双指缩放问题
This commit is contained in:
@@ -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 }
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user