深度画布 矩形工具
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { isBoolean } from "lodash-es";
|
||||
import { OperationType, OperationTypes } from "../../tools/layerHelper";
|
||||
import { RectToolManager } from "../RectToolManager"
|
||||
|
||||
export class CanvasEventManager {
|
||||
constructor(canvas, options = {}) {
|
||||
@@ -22,6 +23,15 @@ export class CanvasEventManager {
|
||||
this.longPressTimer = null;
|
||||
this.longPressThreshold = 500;
|
||||
|
||||
|
||||
const managers = {
|
||||
canvasManager: this.canvasManager,
|
||||
stateManager: this.stateManager,
|
||||
toolManager: this.toolManager,
|
||||
layerManager: this.layerManager,
|
||||
}
|
||||
this.rectToolManager = new RectToolManager(managers)
|
||||
|
||||
// 初始化所有事件
|
||||
this.initEvents();
|
||||
}
|
||||
@@ -188,51 +198,65 @@ export class CanvasEventManager {
|
||||
// console.log("==========选择模式鼠标右击画布对象")
|
||||
|
||||
// } else
|
||||
if (
|
||||
opt.e.altKey ||
|
||||
opt.e.which === 2 ||
|
||||
this.toolManager.currentTool.value === OperationType.PAN
|
||||
) {
|
||||
const currentTool = this.toolManager.currentTool.value;
|
||||
if (currentTool === OperationType.DRAW) {
|
||||
// 绘画模式
|
||||
} else if (currentTool === OperationType.ERASER) {
|
||||
// 橡皮擦模式
|
||||
} else if (currentTool === OperationType.SELECTBOX) {
|
||||
// 选择框模式
|
||||
} else if (currentTool === OperationType.RECTANGLE) {
|
||||
// 矩形模式
|
||||
this.rectToolManager.mouseDownEvent(opt);
|
||||
} else if (opt.e.altKey || opt.e.which === 2 || currentTool === OperationType.PAN) {
|
||||
this.canvas.isDragging = true;
|
||||
this.canvas.lastPosX = opt.e.clientX;
|
||||
this.canvas.lastPosY = opt.e.clientY;
|
||||
this.canvas.defaultCursor = "grabbing";
|
||||
// this.canvas.defaultCursor = "grabbing";
|
||||
|
||||
// 记录拖动开始时间和位置,用于计算速度
|
||||
this.dragStartTime = Date.now();
|
||||
this.lastMousePositions = []; // 重置位置历史
|
||||
|
||||
if (this.canvas.isDragging) {
|
||||
this.canvas.selection = false;
|
||||
this.canvas.renderAll();
|
||||
}
|
||||
this.canvas.selection = false;
|
||||
this.canvas.renderAll();
|
||||
}
|
||||
});
|
||||
|
||||
// 鼠标移动事件
|
||||
this.canvas.on("mouse:move", (opt) => {
|
||||
if (!this.canvas.isDragging) return;
|
||||
const currentTool = this.toolManager.currentTool.value;
|
||||
if (currentTool === OperationType.DRAW) {
|
||||
// 绘画模式
|
||||
} else if (currentTool === OperationType.ERASER) {
|
||||
// 橡皮擦模式
|
||||
} else if (currentTool === OperationType.SELECTBOX) {
|
||||
// 选择框模式
|
||||
} else if (currentTool === OperationType.RECTANGLE) {
|
||||
// 矩形模式
|
||||
this.rectToolManager.mouseMoveEvent(opt);
|
||||
} else if (this.canvas.isDragging) {
|
||||
const vpt = this.canvas.viewportTransform;
|
||||
vpt[4] += opt.e.clientX - this.canvas.lastPosX;
|
||||
vpt[5] += opt.e.clientY - this.canvas.lastPosY;
|
||||
|
||||
const vpt = this.canvas.viewportTransform;
|
||||
vpt[4] += opt.e.clientX - this.canvas.lastPosX;
|
||||
vpt[5] += opt.e.clientY - this.canvas.lastPosY;
|
||||
// 记录鼠标位置和时间,用于计算惯性
|
||||
const now = Date.now();
|
||||
this.lastMousePositions.push({
|
||||
x: opt.e.clientX,
|
||||
y: opt.e.clientY,
|
||||
time: now,
|
||||
});
|
||||
|
||||
// 记录鼠标位置和时间,用于计算惯性
|
||||
const now = Date.now();
|
||||
this.lastMousePositions.push({
|
||||
x: opt.e.clientX,
|
||||
y: opt.e.clientY,
|
||||
time: now,
|
||||
});
|
||||
|
||||
// 保持历史记录在限定数量内
|
||||
if (this.lastMousePositions.length > this.positionHistoryLimit) {
|
||||
this.lastMousePositions.shift();
|
||||
// 保持历史记录在限定数量内
|
||||
if (this.lastMousePositions.length > this.positionHistoryLimit) {
|
||||
this.lastMousePositions.shift();
|
||||
}
|
||||
this.canvas.setViewportTransform(vpt);
|
||||
this.canvas.renderAll();
|
||||
this.canvas.lastPosX = opt.e.clientX;
|
||||
this.canvas.lastPosY = opt.e.clientY;
|
||||
}
|
||||
this.canvas.setViewportTransform(vpt);
|
||||
this.canvas.renderAll();
|
||||
this.canvas.lastPosX = opt.e.clientX;
|
||||
this.canvas.lastPosY = opt.e.clientY;
|
||||
});
|
||||
|
||||
// 鼠标抬起事件
|
||||
@@ -284,171 +308,197 @@ export class CanvasEventManager {
|
||||
// 使用标准的mouse事件,Fabric.js会自动处理触摸转换
|
||||
this.canvas.on("mouse:down", (opt) => {
|
||||
// 只在PAN模式下处理触摸事件
|
||||
if (this.toolManager.currentTool.value !== OperationType.PAN) {
|
||||
return;
|
||||
}
|
||||
const currentTool = this.toolManager.currentTool.value;
|
||||
if (currentTool === OperationType.DRAW) {
|
||||
// 绘画模式
|
||||
} else if (currentTool === OperationType.ERASER) {
|
||||
// 橡皮擦模式
|
||||
} else if (currentTool === OperationType.SELECTBOX) {
|
||||
// 选择框模式
|
||||
} else if (currentTool === OperationType.RECTANGLE) {
|
||||
// 矩形模式
|
||||
this.rectToolManager.mouseDownEvent(opt);
|
||||
} else if (currentTool === OperationType.PAN) {
|
||||
|
||||
// 平滑停止任何正在进行的惯性动画
|
||||
this.stopInertiaAnimation(true);
|
||||
// 平滑停止任何正在进行的惯性动画
|
||||
this.stopInertiaAnimation(true);
|
||||
|
||||
// 检查是否是触摸事件
|
||||
const isTouch = opt.e.type && opt.e.type.includes("touch");
|
||||
const touches =
|
||||
opt.e.touches || (opt.e.originalEvent && opt.e.originalEvent.touches);
|
||||
// 检查是否是触摸事件
|
||||
const isTouch = opt.e.type && opt.e.type.includes("touch");
|
||||
const touches =
|
||||
opt.e.touches || (opt.e.originalEvent && opt.e.originalEvent.touches);
|
||||
|
||||
if (isTouch && touches && touches.length === 2) {
|
||||
// 双指触摸 - 用于缩放
|
||||
this.touchState.isZooming = true;
|
||||
this.touchState.initialDistance = this.getTouchDistance(
|
||||
touches[0],
|
||||
touches[1]
|
||||
);
|
||||
this.touchState.initialZoom = this.canvas.getZoom();
|
||||
if (isTouch && touches && touches.length === 2) {
|
||||
// 双指触摸 - 用于缩放
|
||||
this.touchState.isZooming = true;
|
||||
this.touchState.initialDistance = this.getTouchDistance(
|
||||
touches[0],
|
||||
touches[1]
|
||||
);
|
||||
this.touchState.initialZoom = this.canvas.getZoom();
|
||||
|
||||
// 计算缩放中心点
|
||||
const centerX = (touches[0].clientX + touches[1].clientX) / 2;
|
||||
const centerY = (touches[0].clientY + touches[1].clientY) / 2;
|
||||
this.touchState.zoomCenter = { x: centerX, y: centerY };
|
||||
// 计算缩放中心点
|
||||
const centerX = (touches[0].clientX + touches[1].clientX) / 2;
|
||||
const centerY = (touches[0].clientY + touches[1].clientY) / 2;
|
||||
this.touchState.zoomCenter = { x: centerX, y: centerY };
|
||||
|
||||
opt.e.preventDefault();
|
||||
} else if (isTouch && touches && touches.length === 1) {
|
||||
// 单指触摸 - 用于拖拽
|
||||
this.canvas.isDragging = true;
|
||||
this.canvas.lastPosX = touches[0].clientX;
|
||||
this.canvas.lastPosY = touches[0].clientY;
|
||||
opt.e.preventDefault();
|
||||
} else if (isTouch && touches && touches.length === 1) {
|
||||
// 单指触摸 - 用于拖拽
|
||||
this.canvas.isDragging = true;
|
||||
this.canvas.lastPosX = touches[0].clientX;
|
||||
this.canvas.lastPosY = touches[0].clientY;
|
||||
|
||||
this.dragStartTime = Date.now();
|
||||
this.lastMousePositions = [];
|
||||
this.dragStartTime = Date.now();
|
||||
this.lastMousePositions = [];
|
||||
|
||||
this.canvas.selection = false;
|
||||
opt.e.preventDefault();
|
||||
} else if (!isTouch) {
|
||||
// 鼠标事件 - 用于拖拽
|
||||
this.canvas.isDragging = true;
|
||||
this.canvas.lastPosX = opt.e.clientX;
|
||||
this.canvas.lastPosY = opt.e.clientY;
|
||||
this.canvas.selection = false;
|
||||
opt.e.preventDefault();
|
||||
} else if (!isTouch) {
|
||||
// 鼠标事件 - 用于拖拽
|
||||
this.canvas.isDragging = true;
|
||||
this.canvas.lastPosX = opt.e.clientX;
|
||||
this.canvas.lastPosY = opt.e.clientY;
|
||||
|
||||
this.dragStartTime = Date.now();
|
||||
this.lastMousePositions = [];
|
||||
this.dragStartTime = Date.now();
|
||||
this.lastMousePositions = [];
|
||||
|
||||
this.canvas.selection = false;
|
||||
opt.e.preventDefault();
|
||||
this.canvas.selection = false;
|
||||
opt.e.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 触摸移动事件 - 优化性能
|
||||
this.canvas.on("mouse:move", (opt) => {
|
||||
// 只在PAN模式下处理
|
||||
if (this.toolManager.currentTool.value !== OperationType.PAN) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否是触摸事件
|
||||
const isTouch = opt.e.type && opt.e.type.includes("touch");
|
||||
const touches =
|
||||
opt.e.touches || (opt.e.originalEvent && opt.e.originalEvent.touches);
|
||||
const currentTool = this.toolManager.currentTool.value;
|
||||
if (currentTool === OperationType.DRAW) {
|
||||
// 绘画模式
|
||||
} else if (currentTool === OperationType.ERASER) {
|
||||
// 橡皮擦模式
|
||||
} else if (currentTool === OperationType.SELECTBOX) {
|
||||
// 选择框模式
|
||||
} else if (currentTool === OperationType.RECTANGLE) {
|
||||
// 矩形模式
|
||||
this.rectToolManager.mouseMoveEvent(opt);
|
||||
} else if (currentTool === OperationType.PAN) {
|
||||
|
||||
if (
|
||||
isTouch &&
|
||||
touches &&
|
||||
touches.length === 2 &&
|
||||
this.touchState.isZooming
|
||||
) {
|
||||
// 双指缩放处理 - 修复抖动问题
|
||||
const currentDistance = this.getTouchDistance(touches[0], touches[1]);
|
||||
// 检查是否是触摸事件
|
||||
const isTouch = opt.e.type && opt.e.type.includes("touch");
|
||||
const touches =
|
||||
opt.e.touches || (opt.e.originalEvent && opt.e.originalEvent.touches);
|
||||
|
||||
// 防止除零和异常值
|
||||
if (this.touchState.initialDistance === 0 || currentDistance === 0) {
|
||||
if (
|
||||
isTouch &&
|
||||
touches &&
|
||||
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;
|
||||
|
||||
// 限制缩放范围
|
||||
const clampedZoom = Math.max(0.1, Math.min(5, newZoom));
|
||||
|
||||
// 使用缩放中心点进行缩放
|
||||
const point = new fabric.Point(
|
||||
this.touchState.zoomCenter.x,
|
||||
this.touchState.zoomCenter.y
|
||||
);
|
||||
|
||||
this.canvas.zoomToPoint(point, clampedZoom);
|
||||
opt.e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
const scale = currentDistance / this.touchState.initialDistance;
|
||||
if (!this.canvas.isDragging) return;
|
||||
|
||||
// 防止抖动:忽略微小的变化
|
||||
if (Math.abs(scale - 1) < 0.01) {
|
||||
let currentX, currentY;
|
||||
|
||||
if (isTouch && touches && touches.length === 1) {
|
||||
// 单指触摸移动
|
||||
currentX = touches[0].clientX;
|
||||
currentY = touches[0].clientY;
|
||||
} else if (!isTouch) {
|
||||
// 鼠标移动
|
||||
currentX = opt.e.clientX;
|
||||
currentY = opt.e.clientY;
|
||||
} else {
|
||||
return; // 忽略其他情况
|
||||
}
|
||||
|
||||
// 优化:减少频繁的DOM操作
|
||||
const deltaX = currentX - this.canvas.lastPosX;
|
||||
const deltaY = currentY - this.canvas.lastPosY;
|
||||
|
||||
// 只有移动距离足够大时才更新
|
||||
if (Math.abs(deltaX) < 1 && Math.abs(deltaY) < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newZoom = this.touchState.initialZoom * scale;
|
||||
const vpt = this.canvas.viewportTransform;
|
||||
vpt[4] += deltaX;
|
||||
vpt[5] += deltaY;
|
||||
|
||||
// 限制缩放范围
|
||||
const clampedZoom = Math.max(0.1, Math.min(5, newZoom));
|
||||
// 优化:减少历史记录频率
|
||||
const now = Date.now();
|
||||
if (now - this.touchState.lastTouchTime > 16) {
|
||||
// 约60fps
|
||||
this.lastMousePositions.push({
|
||||
x: currentX,
|
||||
y: currentY,
|
||||
time: now,
|
||||
});
|
||||
|
||||
// 使用缩放中心点进行缩放
|
||||
const point = new fabric.Point(
|
||||
this.touchState.zoomCenter.x,
|
||||
this.touchState.zoomCenter.y
|
||||
);
|
||||
if (this.lastMousePositions.length > this.positionHistoryLimit) {
|
||||
this.lastMousePositions.shift();
|
||||
}
|
||||
|
||||
this.canvas.zoomToPoint(point, clampedZoom);
|
||||
this.touchState.lastTouchTime = now;
|
||||
}
|
||||
this.canvas.setViewportTransform(vpt);
|
||||
this.canvas.requestRenderAll(); // 使用requestRenderAll代替renderAll
|
||||
this.canvas.lastPosX = currentX;
|
||||
this.canvas.lastPosY = currentY;
|
||||
opt.e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.canvas.isDragging) return;
|
||||
|
||||
let currentX, currentY;
|
||||
|
||||
if (isTouch && touches && touches.length === 1) {
|
||||
// 单指触摸移动
|
||||
currentX = touches[0].clientX;
|
||||
currentY = touches[0].clientY;
|
||||
} else if (!isTouch) {
|
||||
// 鼠标移动
|
||||
currentX = opt.e.clientX;
|
||||
currentY = opt.e.clientY;
|
||||
} else {
|
||||
return; // 忽略其他情况
|
||||
}
|
||||
|
||||
// 优化:减少频繁的DOM操作
|
||||
const deltaX = currentX - this.canvas.lastPosX;
|
||||
const deltaY = currentY - this.canvas.lastPosY;
|
||||
|
||||
// 只有移动距离足够大时才更新
|
||||
if (Math.abs(deltaX) < 1 && Math.abs(deltaY) < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const vpt = this.canvas.viewportTransform;
|
||||
vpt[4] += deltaX;
|
||||
vpt[5] += deltaY;
|
||||
|
||||
// 优化:减少历史记录频率
|
||||
const now = Date.now();
|
||||
if (now - this.touchState.lastTouchTime > 16) {
|
||||
// 约60fps
|
||||
this.lastMousePositions.push({
|
||||
x: currentX,
|
||||
y: currentY,
|
||||
time: now,
|
||||
});
|
||||
|
||||
if (this.lastMousePositions.length > this.positionHistoryLimit) {
|
||||
this.lastMousePositions.shift();
|
||||
}
|
||||
|
||||
this.touchState.lastTouchTime = now;
|
||||
}
|
||||
this.canvas.setViewportTransform(vpt);
|
||||
this.canvas.requestRenderAll(); // 使用requestRenderAll代替renderAll
|
||||
this.canvas.lastPosX = currentX;
|
||||
this.canvas.lastPosY = currentY;
|
||||
opt.e.preventDefault();
|
||||
});
|
||||
|
||||
// 触摸结束事件
|
||||
this.canvas.on("mouse:up", (opt) => {
|
||||
// 只在PAN模式下处理
|
||||
if (this.toolManager.currentTool.value !== OperationType.PAN) {
|
||||
return;
|
||||
const currentTool = this.toolManager.currentTool.value;
|
||||
if (currentTool === OperationType.DRAW) {
|
||||
// 绘画模式
|
||||
} else if (currentTool === OperationType.ERASER) {
|
||||
// 橡皮擦模式
|
||||
} else if (currentTool === OperationType.SELECTBOX) {
|
||||
// 选择框模式
|
||||
} else if (currentTool === OperationType.RECTANGLE) {
|
||||
// 矩形模式
|
||||
this.rectToolManager.mouseUpEvent(opt);
|
||||
} else if (currentTool === OperationType.PAN) {
|
||||
|
||||
// 重置触摸状态
|
||||
this.touchState.isZooming = false;
|
||||
this.touchState.initialDistance = 0;
|
||||
|
||||
this.handleDragEnd(opt, true);
|
||||
}
|
||||
|
||||
// 重置触摸状态
|
||||
this.touchState.isZooming = false;
|
||||
this.touchState.initialDistance = 0;
|
||||
|
||||
this.handleDragEnd(opt, true);
|
||||
});
|
||||
|
||||
// 添加原生触摸事件监听器作为备用方案
|
||||
@@ -602,24 +652,28 @@ export class CanvasEventManager {
|
||||
* 处理拖动结束(鼠标抬起或触摸结束)
|
||||
*/
|
||||
handleDragEnd(opt, isTouch = false) {
|
||||
if (this.canvas.isDragging) {
|
||||
// 使用动画管理器处理惯性效果
|
||||
const currentTool = this.toolManager.currentTool.value;
|
||||
if (currentTool === OperationType.DRAW) {
|
||||
// 绘画模式
|
||||
} else if (currentTool === OperationType.ERASER) {
|
||||
// 橡皮擦模式
|
||||
} else if (currentTool === OperationType.SELECTBOX) {
|
||||
// 选择框模式
|
||||
} else if (currentTool === OperationType.RECTANGLE) {
|
||||
// 矩形模式
|
||||
this.rectToolManager.mouseUpEvent(opt);
|
||||
} else if (this.canvas.isDragging) {
|
||||
// if (this.lastMousePositions.length > 1 && opt && opt.e) {
|
||||
// this.animationManager.applyInertiaEffect(
|
||||
// this.lastMousePositions,
|
||||
// isTouch
|
||||
// );
|
||||
// }
|
||||
this.canvas.isDragging = false;
|
||||
const vpt = this.canvas.viewportTransform;
|
||||
this.canvas.setViewportTransform(vpt);
|
||||
this.canvas.renderAll();
|
||||
}
|
||||
|
||||
this.canvas.isDragging = false;
|
||||
|
||||
if (this.toolManager) {
|
||||
// this.toolManager.restoreSelectionState(); // 恢复选择状态
|
||||
}
|
||||
const vpt = this.canvas.viewportTransform;
|
||||
this.canvas.setViewportTransform(vpt);
|
||||
this.canvas.renderAll();
|
||||
}
|
||||
|
||||
setupSelectionEvents() {
|
||||
@@ -827,30 +881,6 @@ export class CanvasEventManager {
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
// 移除所有事件监听
|
||||
this.canvas.off();
|
||||
|
||||
// 清理 Mac 专用的原生事件监听器
|
||||
if (this.deviceInfo.isMac && this.canvas.upperCanvasEl) {
|
||||
const upperCanvas = this.canvas.upperCanvasEl;
|
||||
|
||||
// 移除手势事件监听器
|
||||
upperCanvas.removeEventListener("gesturestart", null);
|
||||
upperCanvas.removeEventListener("gesturechange", null);
|
||||
upperCanvas.removeEventListener("gestureend", null);
|
||||
upperCanvas.removeEventListener("wheel", null);
|
||||
}
|
||||
|
||||
// 清除计时器
|
||||
if (this.longPressTimer) {
|
||||
clearTimeout(this.longPressTimer);
|
||||
this.longPressTimer = null;
|
||||
}
|
||||
|
||||
// 停止所有动画
|
||||
this.stopInertiaAnimation();
|
||||
}
|
||||
|
||||
/**
|
||||
* 精确检测设备类型,区分 PC、Mac、平板和移动设备
|
||||
@@ -1032,4 +1062,30 @@ export class CanvasEventManager {
|
||||
{ passive: true }
|
||||
);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.rectToolManager?.dispose()
|
||||
// 移除所有事件监听
|
||||
this.canvas.off();
|
||||
|
||||
// 清理 Mac 专用的原生事件监听器
|
||||
if (this.deviceInfo.isMac && this.canvas.upperCanvasEl) {
|
||||
const upperCanvas = this.canvas.upperCanvasEl;
|
||||
|
||||
// 移除手势事件监听器
|
||||
upperCanvas.removeEventListener("gesturestart", null);
|
||||
upperCanvas.removeEventListener("gesturechange", null);
|
||||
upperCanvas.removeEventListener("gestureend", null);
|
||||
upperCanvas.removeEventListener("wheel", null);
|
||||
}
|
||||
|
||||
// 清除计时器
|
||||
if (this.longPressTimer) {
|
||||
clearTimeout(this.longPressTimer);
|
||||
this.longPressTimer = null;
|
||||
}
|
||||
|
||||
// 停止所有动画
|
||||
this.stopInertiaAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user