2025-06-09 10:25:54 +08:00
|
|
|
|
import { BrushStore } from "../store/BrushStore";
|
|
|
|
|
|
import { BrushManager } from "./brushes/brushManager";
|
2025-06-24 01:54:37 +08:00
|
|
|
|
import { BrushIndicator } from "./BrushIndicator";
|
2025-06-09 10:25:54 +08:00
|
|
|
|
import { ToolCommand } from "../commands/ToolCommands";
|
2025-06-22 13:52:28 +08:00
|
|
|
|
import { CreateTextCommand } from "../commands/TextCommands";
|
2025-07-24 21:37:21 +08:00
|
|
|
|
import { findLayerRecursively, OperationType } from "../utils/layerHelper";
|
2025-06-09 10:25:54 +08:00
|
|
|
|
import CanvasConfig from "../config/canvasConfig";
|
2025-06-18 11:05:23 +08:00
|
|
|
|
import { fabric } from "fabric-with-all";
|
|
|
|
|
|
import { InitLiquifyToolCommand } from "../commands/LiquifyCommands";
|
2025-06-22 13:52:28 +08:00
|
|
|
|
import { RasterizeLayerCommand } from "../commands/RasterizeLayerCommand";
|
2025-06-18 11:05:23 +08:00
|
|
|
|
import { message, Modal } from "ant-design-vue";
|
|
|
|
|
|
import { h } from "vue";
|
2025-07-21 21:28:42 +08:00
|
|
|
|
import { findObjectById } from "../utils/helper";
|
2025-06-09 10:25:54 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 工具管理器
|
|
|
|
|
|
* 负责管理编辑器中的各种工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class ToolManager {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建工具管理器
|
|
|
|
|
|
* @param {Object} options 配置选项
|
|
|
|
|
|
* @param {Object} options.canvas fabric.js画布实例
|
|
|
|
|
|
* @param {Object} options.commandManager 命令管理器实例
|
|
|
|
|
|
* @param {Object} options.canvasManager 画布管理实例
|
|
|
|
|
|
* @param {Object} options.layerManager 图层管理实例
|
|
|
|
|
|
* @param {Object} options.brushManager 画笔管理器实例(可选,如果不提供会创建一个)
|
|
|
|
|
|
* @param {Object} options.activeTool 当前活动工具的响应式引用
|
|
|
|
|
|
*/
|
|
|
|
|
|
constructor(options = {}) {
|
|
|
|
|
|
this.canvas = options.canvas;
|
|
|
|
|
|
this.commandManager = options.commandManager;
|
|
|
|
|
|
this.canvasManager = options.canvasManager;
|
|
|
|
|
|
this.layerManager = options.layerManager;
|
|
|
|
|
|
this.activeTool = options.activeTool || {
|
|
|
|
|
|
value: OperationType.SELECT,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 红绿图模式状态
|
|
|
|
|
|
this.isRedGreenMode = false;
|
|
|
|
|
|
this.redGreenModeManager = null;
|
|
|
|
|
|
|
|
|
|
|
|
// 使用传入的brushManager或创建新的实例
|
|
|
|
|
|
this.brushManager =
|
|
|
|
|
|
options.brushManager ||
|
|
|
|
|
|
new BrushManager({
|
|
|
|
|
|
canvas: this.canvas,
|
|
|
|
|
|
brushSize: options.brushSize,
|
|
|
|
|
|
layerManager: this.layerManager, // 传入图层管理器引用
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-06-24 01:54:37 +08:00
|
|
|
|
// 初始化笔刷指示器
|
|
|
|
|
|
this.brushIndicator = null;
|
|
|
|
|
|
if (this.canvas) {
|
|
|
|
|
|
this.brushIndicator = new BrushIndicator(this.canvas, {
|
|
|
|
|
|
strokeColor: "rgba(0, 0, 0, 0.6)",
|
|
|
|
|
|
strokeWidth: 1,
|
|
|
|
|
|
fillColor: "rgba(0, 0, 0, 0.1)",
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-09 10:25:54 +08:00
|
|
|
|
// 观察者列表
|
|
|
|
|
|
this.observers = [];
|
|
|
|
|
|
|
|
|
|
|
|
// 工具列表 - 与OperationType保持一致
|
|
|
|
|
|
this.tools = {
|
|
|
|
|
|
// 基础工具
|
|
|
|
|
|
[OperationType.SELECT]: {
|
|
|
|
|
|
name: "选择工具",
|
|
|
|
|
|
icon: "select",
|
|
|
|
|
|
cursor: "default",
|
|
|
|
|
|
shortcut: "V",
|
|
|
|
|
|
setup: this.setupSelectTool.bind(this),
|
|
|
|
|
|
allowedInRedGreen: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
[OperationType.DRAW]: {
|
|
|
|
|
|
name: "画笔工具",
|
|
|
|
|
|
icon: "brush",
|
|
|
|
|
|
cursor: "crosshair",
|
|
|
|
|
|
shortcut: "B",
|
|
|
|
|
|
setup: this.setupBrushTool.bind(this),
|
|
|
|
|
|
allowedInRedGreen: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
[OperationType.ERASER]: {
|
|
|
|
|
|
name: "橡皮擦",
|
|
|
|
|
|
icon: "eraser",
|
|
|
|
|
|
cursor: "crosshair",
|
|
|
|
|
|
shortcut: "E",
|
|
|
|
|
|
setup: this.setupEraserTool.bind(this),
|
|
|
|
|
|
allowedInRedGreen: true, // 红绿图模式允许橡皮擦
|
|
|
|
|
|
},
|
|
|
|
|
|
[OperationType.EYEDROPPER]: {
|
|
|
|
|
|
name: "吸色工具",
|
|
|
|
|
|
icon: "eyedropper",
|
|
|
|
|
|
cursor: "crosshair",
|
|
|
|
|
|
shortcut: "I",
|
|
|
|
|
|
setup: this.setupEyedropperTool.bind(this),
|
|
|
|
|
|
allowedInRedGreen: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
[OperationType.PAN]: {
|
|
|
|
|
|
name: "移动画布",
|
|
|
|
|
|
icon: "hand",
|
|
|
|
|
|
cursor: "grab",
|
|
|
|
|
|
shortcut: "H",
|
|
|
|
|
|
setup: this.setupHandTool.bind(this),
|
|
|
|
|
|
allowedInRedGreen: false, // 红绿图模式不允许PAN
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 套索工具
|
|
|
|
|
|
[OperationType.LASSO]: {
|
|
|
|
|
|
name: "套索工具",
|
|
|
|
|
|
icon: "lasso",
|
|
|
|
|
|
cursor: "crosshair",
|
|
|
|
|
|
shortcut: "L",
|
|
|
|
|
|
setup: this.setupLassoTool.bind(this),
|
|
|
|
|
|
allowedInRedGreen: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
[OperationType.LASSO_RECTANGLE]: {
|
|
|
|
|
|
name: "矩形套索工具",
|
|
|
|
|
|
icon: "lasso",
|
|
|
|
|
|
cursor: "crosshair",
|
|
|
|
|
|
// shortcut: "L",
|
|
|
|
|
|
altKey: true,
|
|
|
|
|
|
setup: this.setupRectangleLassoTool.bind(this),
|
|
|
|
|
|
allowedInRedGreen: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
[OperationType.LASSO_ELLIPSE]: {
|
|
|
|
|
|
name: "椭圆形套索工具",
|
|
|
|
|
|
icon: "lasso",
|
|
|
|
|
|
cursor: "crosshair",
|
|
|
|
|
|
// shortcut: "L",
|
|
|
|
|
|
altKey: true,
|
|
|
|
|
|
setup: this.setupEllipseLassoTool.bind(this),
|
|
|
|
|
|
allowedInRedGreen: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 选区工具 - 只需要矩形选区
|
|
|
|
|
|
[OperationType.AREA_RECTANGLE]: {
|
|
|
|
|
|
name: "矩形选区工具",
|
|
|
|
|
|
icon: "area-rectangle",
|
|
|
|
|
|
cursor: "crosshair",
|
|
|
|
|
|
shortcut: "M",
|
|
|
|
|
|
altKey: true,
|
|
|
|
|
|
setup: this.setupRectangleAreaTool.bind(this),
|
|
|
|
|
|
allowedInRedGreen: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 特效工具
|
|
|
|
|
|
[OperationType.WAVE]: {
|
|
|
|
|
|
name: "波浪工具",
|
|
|
|
|
|
icon: "wave",
|
|
|
|
|
|
cursor: "crosshair",
|
|
|
|
|
|
shortcut: "W",
|
|
|
|
|
|
setup: this.setupWaveTool.bind(this),
|
|
|
|
|
|
allowedInRedGreen: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
[OperationType.LIQUIFY]: {
|
|
|
|
|
|
name: "液化工具",
|
|
|
|
|
|
icon: "liquify",
|
|
|
|
|
|
cursor: "crosshair",
|
|
|
|
|
|
shortcut: "J",
|
|
|
|
|
|
setup: this.setupLiquifyTool.bind(this),
|
|
|
|
|
|
allowedInRedGreen: false, // 红绿图模式不允许液化
|
|
|
|
|
|
},
|
|
|
|
|
|
[OperationType.TEXT]: {
|
|
|
|
|
|
name: "文本工具",
|
|
|
|
|
|
icon: "text",
|
|
|
|
|
|
cursor: "text",
|
|
|
|
|
|
shortcut: "T",
|
|
|
|
|
|
setup: this.setupTextTool.bind(this),
|
|
|
|
|
|
allowedInRedGreen: false, // 红绿图模式不允许文本
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 红绿图模式专用工具
|
|
|
|
|
|
[OperationType.RED_BRUSH]: {
|
|
|
|
|
|
name: "红色笔刷",
|
|
|
|
|
|
icon: "brush",
|
|
|
|
|
|
cursor: "crosshair",
|
|
|
|
|
|
shortcut: "R",
|
|
|
|
|
|
setup: this.setupRedBrushTool.bind(this),
|
|
|
|
|
|
allowedInRedGreen: true,
|
|
|
|
|
|
redGreenOnly: true, // 只在红绿图模式显示
|
|
|
|
|
|
},
|
|
|
|
|
|
[OperationType.GREEN_BRUSH]: {
|
|
|
|
|
|
name: "绿色笔刷",
|
|
|
|
|
|
icon: "brush",
|
|
|
|
|
|
cursor: "crosshair",
|
|
|
|
|
|
shortcut: "G",
|
|
|
|
|
|
setup: this.setupGreenBrushTool.bind(this),
|
|
|
|
|
|
allowedInRedGreen: true,
|
|
|
|
|
|
redGreenOnly: true, // 只在红绿图模式显示
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 记录先前的工具
|
|
|
|
|
|
this.previousTool = null;
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化默认工具
|
|
|
|
|
|
this.setTool(this.activeTool.value);
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化工具快捷键
|
|
|
|
|
|
this.initKeyboardShortcuts();
|
|
|
|
|
|
|
|
|
|
|
|
// 设置文本编辑事件
|
|
|
|
|
|
this.setupTextEditingEvents();
|
|
|
|
|
|
|
|
|
|
|
|
// 添加观察者
|
|
|
|
|
|
// this.addObserver(this.brushManager);
|
|
|
|
|
|
this.addObserver(this.layerManager);
|
|
|
|
|
|
this.addObserver(this.canvasManager);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 添加观察者
|
|
|
|
|
|
* @param {Object} observer 观察者对象,必须实现toolChanged方法
|
|
|
|
|
|
*/
|
|
|
|
|
|
addObserver(observer) {
|
|
|
|
|
|
if (typeof observer.toolChanged === "function") {
|
|
|
|
|
|
this.observers.push(observer);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.warn("Observer must implement toolChanged method");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 移除观察者
|
|
|
|
|
|
* @param {Object} observer 要移除的观察者对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
removeObserver(observer) {
|
|
|
|
|
|
this.observers = this.observers.filter((obs) => obs !== observer);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 通知所有观察者工具已更改
|
|
|
|
|
|
* @param {String} toolId 工具ID
|
|
|
|
|
|
*/
|
|
|
|
|
|
notifyObservers(toolId) {
|
|
|
|
|
|
this.observers.forEach((observer) => {
|
|
|
|
|
|
observer.toolChanged(toolId, this.tools[toolId]);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 初始化工具快捷键
|
|
|
|
|
|
*/
|
|
|
|
|
|
initKeyboardShortcuts() {
|
|
|
|
|
|
// 可以在这里设置工具快捷键的全局监听
|
|
|
|
|
|
// 如需要由外部统一管理键盘事件,则不需要此方法
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理快捷键事件
|
|
|
|
|
|
* @param {KeyboardEvent} event 键盘事件
|
|
|
|
|
|
*/
|
|
|
|
|
|
handleKeyboardShortcut(event) {
|
|
|
|
|
|
const key = event.key.toUpperCase();
|
|
|
|
|
|
const altKey = event.altKey;
|
|
|
|
|
|
const ctrlKey = event.ctrlKey;
|
|
|
|
|
|
const shiftKey = event.shiftKey;
|
|
|
|
|
|
|
|
|
|
|
|
// 当处于输入状态时不触发快捷键
|
2025-07-21 21:28:42 +08:00
|
|
|
|
if (
|
|
|
|
|
|
event.target.tagName === "INPUT" ||
|
|
|
|
|
|
event.target.tagName === "TEXTAREA"
|
|
|
|
|
|
) {
|
2025-06-09 10:25:54 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 在红绿图模式下,只允许特定快捷键
|
|
|
|
|
|
if (this.isRedGreenMode) {
|
|
|
|
|
|
const allowedKeys = ["E", "R", "G"]; // 橡皮擦、红色笔刷、绿色笔刷
|
|
|
|
|
|
if (!allowedKeys.includes(key)) {
|
|
|
|
|
|
return; // 忽略不允许的快捷键
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查找匹配的工具
|
|
|
|
|
|
for (const [toolId, tool] of Object.entries(this.tools)) {
|
|
|
|
|
|
if (tool.shortcut && tool.shortcut.toUpperCase() === key) {
|
|
|
|
|
|
// 检查可能的辅助键要求
|
|
|
|
|
|
if (
|
|
|
|
|
|
(tool.altKey && !altKey) ||
|
|
|
|
|
|
(tool.ctrlKey && !ctrlKey) ||
|
|
|
|
|
|
(tool.shiftKey && !shiftKey)
|
|
|
|
|
|
) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 在红绿图模式下检查工具可用性
|
2025-07-21 21:28:42 +08:00
|
|
|
|
if (
|
|
|
|
|
|
this.isRedGreenMode &&
|
|
|
|
|
|
!this.isToolAvailableInRedGreenMode(toolId)
|
|
|
|
|
|
) {
|
2025-06-09 10:25:54 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 切换到该工具
|
|
|
|
|
|
this.setToolWithCommand(toolId);
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取所有可用工具列表
|
|
|
|
|
|
* @returns {Array} 工具列表
|
|
|
|
|
|
*/
|
|
|
|
|
|
getTools() {
|
|
|
|
|
|
return Object.keys(this.tools).map((key) => {
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: key,
|
|
|
|
|
|
...this.tools[key],
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取当前工具
|
|
|
|
|
|
* @returns {String} 当前工具ID
|
|
|
|
|
|
*/
|
|
|
|
|
|
getCurrentTool() {
|
|
|
|
|
|
return this.activeTool.value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置当前活动工具
|
|
|
|
|
|
* @param {String} toolId 工具ID
|
|
|
|
|
|
*/
|
|
|
|
|
|
setTool(toolId) {
|
|
|
|
|
|
// 检查工具是否存在
|
|
|
|
|
|
if (!this.tools[toolId]) {
|
|
|
|
|
|
console.error(`工具 '${toolId}' 不存在`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 在红绿图模式下检查工具可用性
|
|
|
|
|
|
if (this.isRedGreenMode && !this.isToolAvailableInRedGreenMode(toolId)) {
|
|
|
|
|
|
console.warn(`工具 '${toolId}' 在红绿图模式下不可用`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 在普通模式下检查是否为红绿图专用工具
|
|
|
|
|
|
if (!this.isRedGreenMode && this.isRedGreenOnlyTool(toolId)) {
|
|
|
|
|
|
console.warn(`工具 '${toolId}' 只能在红绿图模式下使用`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 保存先前的工具
|
|
|
|
|
|
this.previousTool = this.activeTool.value;
|
|
|
|
|
|
|
2025-07-24 21:37:21 +08:00
|
|
|
|
// 取消画布的选中状态
|
|
|
|
|
|
this.canvas?.discardActiveObject();
|
|
|
|
|
|
this.canvasManager?.layerManager?.updateLayersObjectsInteractivity?.();
|
|
|
|
|
|
this.canvas?.renderAll();
|
|
|
|
|
|
|
2025-06-24 01:54:37 +08:00
|
|
|
|
// 如果切换到非画笔工具,禁用笔刷指示器
|
|
|
|
|
|
if (!this._shouldShowBrushIndicator(toolId)) {
|
|
|
|
|
|
this._disableBrushIndicator();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-09 10:25:54 +08:00
|
|
|
|
// 设置新工具
|
|
|
|
|
|
this.activeTool.value = toolId;
|
|
|
|
|
|
|
|
|
|
|
|
// 设置光标
|
|
|
|
|
|
if (this.canvas) {
|
|
|
|
|
|
this.canvas.defaultCursor = this.tools[toolId].cursor;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置工具特定的状态
|
|
|
|
|
|
const tool = this.tools[toolId];
|
|
|
|
|
|
if (tool && typeof tool.setup === "function") {
|
|
|
|
|
|
tool.setup();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 通知选区管理器工具已改变
|
|
|
|
|
|
if (this.canvasManager && this.canvasManager.selectionManager) {
|
|
|
|
|
|
this.canvasManager.selectionManager.setCurrentTool(toolId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 通知观察者
|
|
|
|
|
|
this.notifyObservers(toolId);
|
|
|
|
|
|
|
|
|
|
|
|
return this.activeTool.value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 通过命令模式设置工具
|
|
|
|
|
|
* @param {String} toolId 工具ID
|
|
|
|
|
|
*/
|
|
|
|
|
|
setToolWithCommand(toolId, options = {}) {
|
|
|
|
|
|
if (!this.commandManager) {
|
|
|
|
|
|
this.setTool(toolId);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建工具切换命令
|
|
|
|
|
|
const command = new ToolCommand({
|
|
|
|
|
|
toolManager: this,
|
|
|
|
|
|
tool: toolId,
|
|
|
|
|
|
previousTool: this.activeTool.value,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-06-18 11:05:23 +08:00
|
|
|
|
command.undoable = options.undoable !== undefined ? options.undoable : true;
|
|
|
|
|
|
|
2025-06-09 10:25:54 +08:00
|
|
|
|
// 执行命令
|
|
|
|
|
|
this.commandManager.execute(command, { ...options });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 恢复当前工具的选择状态
|
|
|
|
|
|
* 在拖拽结束时调用,确保canvas.selection状态与当前工具一致
|
|
|
|
|
|
*/
|
|
|
|
|
|
restoreSelectionState() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
const currentTool = this.activeTool.value;
|
|
|
|
|
|
const tool = this.tools[currentTool];
|
|
|
|
|
|
|
|
|
|
|
|
// 根据当前工具设置selection状态
|
|
|
|
|
|
if (currentTool === OperationType.SELECT) {
|
|
|
|
|
|
this.canvas.selection = true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 对于大多数工具,selection应该是false
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如有必要,可以调用当前工具的setup方法来全面恢复状态
|
|
|
|
|
|
if (tool && typeof tool.setup === "function") {
|
|
|
|
|
|
tool.setup();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 切换回先前使用的工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
togglePreviousTool() {
|
|
|
|
|
|
if (this.previousTool && this.previousTool !== this.activeTool.value) {
|
|
|
|
|
|
this.setTool(this.previousTool);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置选择工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupSelectTool() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
this.canvas.isDrawingMode = false;
|
|
|
|
|
|
this.canvas.selection = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置画笔工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupBrushTool() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.isDrawingMode = true;
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 确保有笔刷管理器
|
|
|
|
|
|
if (this.brushManager) {
|
|
|
|
|
|
// 检查画笔是否正在更新中
|
|
|
|
|
|
if (this.brushManager.isUpdatingBrush) {
|
|
|
|
|
|
console.warn("画笔正在更新中,请稍候...");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (BrushStore) {
|
|
|
|
|
|
// 同步基本属性
|
|
|
|
|
|
this.brushManager.setBrushSize(BrushStore.state.size);
|
|
|
|
|
|
this.brushManager.setBrushColor(BrushStore.state.color);
|
|
|
|
|
|
this.brushManager.setBrushOpacity(BrushStore.state.opacity);
|
|
|
|
|
|
|
|
|
|
|
|
// 同步笔刷类型 - 修复方法名,使用正确的setBrushType方法
|
2025-06-23 18:01:33 +08:00
|
|
|
|
this.brushManager.setBrushType(
|
|
|
|
|
|
BrushStore.state.type === "eraser" ? "pencil" : BrushStore.state.type
|
|
|
|
|
|
);
|
2025-06-09 10:25:54 +08:00
|
|
|
|
|
|
|
|
|
|
// 同步材质设置
|
|
|
|
|
|
if (BrushStore.state.textureEnabled && BrushStore.state.texturePath) {
|
|
|
|
|
|
this.brushManager.setTexturePath(BrushStore.state.texturePath);
|
|
|
|
|
|
this.brushManager.setTextureScale(BrushStore.state.textureScale);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新应用到画布
|
|
|
|
|
|
this.brushManager.updateBrush();
|
|
|
|
|
|
}
|
2025-06-24 01:54:37 +08:00
|
|
|
|
|
|
|
|
|
|
// 启用笔刷指示器并同步颜色
|
|
|
|
|
|
this._enableBrushIndicator();
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置橡皮擦工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupEraserTool() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
this.canvas.isDrawingMode = true;
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 确保有笔刷管理器
|
|
|
|
|
|
if (this.brushManager) {
|
|
|
|
|
|
this.brushManager.createEraser();
|
|
|
|
|
|
}
|
2025-06-24 01:54:37 +08:00
|
|
|
|
|
|
|
|
|
|
// 启用笔刷指示器
|
|
|
|
|
|
this._enableBrushIndicator();
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置吸色工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupEyedropperTool() {
|
|
|
|
|
|
if (!this.canvas || !this.brushManager) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.isDrawingMode = false;
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 保存当前工具,以便吸色完成后还原
|
|
|
|
|
|
const currentTool = this.activeTool.value;
|
|
|
|
|
|
|
|
|
|
|
|
// 使用吸色工具
|
|
|
|
|
|
this.brushManager.createEyedropper((color) => {
|
|
|
|
|
|
// 设置画笔颜色
|
|
|
|
|
|
this.brushManager.setBrushColor(color);
|
|
|
|
|
|
|
|
|
|
|
|
// 吸色完成后,恢复到之前的工具
|
|
|
|
|
|
this.setTool(currentTool);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置移动画布工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupHandTool() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.isDrawingMode = false;
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 设置画布为可拖动状态
|
|
|
|
|
|
this.canvas.defaultCursor = "grab";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置套索工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupLassoTool() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.isDrawingMode = false;
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 通知选区管理器切换到自由套索工具
|
|
|
|
|
|
if (this.canvasManager && this.canvasManager.selectionManager) {
|
|
|
|
|
|
this.canvasManager.selectionManager.setCurrentTool(OperationType.LASSO);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置矩形套索工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupRectangleLassoTool() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.isDrawingMode = false;
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 通知选区管理器切换到矩形套索工具
|
|
|
|
|
|
if (this.canvasManager && this.canvasManager.selectionManager) {
|
2025-07-21 21:28:42 +08:00
|
|
|
|
this.canvasManager.selectionManager.setCurrentTool(
|
|
|
|
|
|
OperationType.LASSO_RECTANGLE
|
|
|
|
|
|
);
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置椭圆形套索工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupEllipseLassoTool() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.isDrawingMode = false;
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 通知选区管理器切换到椭圆套索工具
|
|
|
|
|
|
if (this.canvasManager && this.canvasManager.selectionManager) {
|
2025-07-21 21:28:42 +08:00
|
|
|
|
this.canvasManager.selectionManager.setCurrentTool(
|
|
|
|
|
|
OperationType.LASSO_ELLIPSE
|
|
|
|
|
|
);
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置自由选区工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupCustomAreaTool() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.isDrawingMode = false;
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 通知选区管理器切换到椭圆套索工具
|
|
|
|
|
|
if (this.canvasManager && this.canvasManager.selectionManager) {
|
2025-07-21 21:28:42 +08:00
|
|
|
|
this.canvasManager.selectionManager.setCurrentTool(
|
|
|
|
|
|
OperationType.AREA_CUSTOM
|
|
|
|
|
|
);
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置矩形选区工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupRectangleAreaTool() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.isDrawingMode = false;
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
|
|
|
|
|
|
// // 设置矩形选区模式
|
|
|
|
|
|
// // 这里需要具体的矩形选区工具实现
|
|
|
|
|
|
console.log("矩形选区工具已激活");
|
|
|
|
|
|
|
|
|
|
|
|
if (this.canvasManager && this.canvasManager.selectionManager) {
|
2025-07-21 21:28:42 +08:00
|
|
|
|
this.canvasManager.selectionManager.setCurrentTool(
|
|
|
|
|
|
OperationType.AREA_RECTANGLE
|
|
|
|
|
|
);
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置波浪工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupWaveTool() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.isDrawingMode = false;
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置液化工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupLiquifyTool() {
|
|
|
|
|
|
if (!this.canvas || !this.layerManager) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.isDrawingMode = false;
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前活动图层
|
|
|
|
|
|
const activeLayerId = this.layerManager.getActiveLayerId();
|
|
|
|
|
|
|
|
|
|
|
|
// 准备液化面板显示的详情信息
|
|
|
|
|
|
let panelDetail = {
|
|
|
|
|
|
activeLayerId: activeLayerId,
|
|
|
|
|
|
layerStatus: null,
|
|
|
|
|
|
canLiquify: false,
|
|
|
|
|
|
targetObject: null,
|
|
|
|
|
|
originalImageData: null,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 如果有活动图层,检查其状态
|
|
|
|
|
|
if (activeLayerId) {
|
|
|
|
|
|
const liquifyManager = this.canvasManager?.liquifyManager;
|
|
|
|
|
|
if (liquifyManager) {
|
|
|
|
|
|
// 检查图层状态
|
|
|
|
|
|
const checkResult = liquifyManager.checkLayerForLiquify(activeLayerId);
|
|
|
|
|
|
panelDetail.layerStatus = checkResult;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取图层对象
|
2025-07-24 21:37:21 +08:00
|
|
|
|
// const layer = this.layerManager.getLayerById(activeLayerId);
|
|
|
|
|
|
const { layer } = findLayerRecursively(
|
|
|
|
|
|
this.layerManager.layers?.value ?? this.layerManager.layers,
|
|
|
|
|
|
activeLayerId
|
|
|
|
|
|
);
|
2025-06-09 10:25:54 +08:00
|
|
|
|
|
|
|
|
|
|
// 检查图层是否为空
|
|
|
|
|
|
if (!checkResult.isEmpty) {
|
|
|
|
|
|
// 图层不为空,判断是否可直接液化或需要栅格化
|
|
|
|
|
|
if (checkResult.valid) {
|
|
|
|
|
|
// 可以直接液化 (单个图像对象)
|
|
|
|
|
|
panelDetail.canLiquify = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 设置目标对象
|
|
|
|
|
|
if (layer) {
|
|
|
|
|
|
if (layer.isBackground || layer.type === "background") {
|
2025-07-21 21:28:42 +08:00
|
|
|
|
panelDetail.targetObject = findObjectById(
|
|
|
|
|
|
this.canvas,
|
|
|
|
|
|
layer.fabricObject?.id
|
|
|
|
|
|
)?.object;
|
|
|
|
|
|
} else if (
|
|
|
|
|
|
layer.fabricObjects &&
|
|
|
|
|
|
layer.fabricObjects.length > 0
|
|
|
|
|
|
) {
|
|
|
|
|
|
panelDetail.targetObject = findObjectById(
|
|
|
|
|
|
this.canvas,
|
|
|
|
|
|
layer.fabricObjects?.[0]?.id
|
|
|
|
|
|
)?.object;
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 准备液化环境,获取原始图像数据
|
|
|
|
|
|
if (panelDetail.targetObject && liquifyManager) {
|
|
|
|
|
|
liquifyManager.initialize({
|
|
|
|
|
|
canvas: this.canvas,
|
|
|
|
|
|
layerManager: this.layerManager,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 异步获取原始图像数据
|
|
|
|
|
|
liquifyManager
|
|
|
|
|
|
.prepareForLiquify(panelDetail.targetObject)
|
|
|
|
|
|
.then((result) => {
|
|
|
|
|
|
if (result && result.originalImageData) {
|
|
|
|
|
|
// 当获取到原始图像数据后触发更新
|
|
|
|
|
|
const updatedDetail = {
|
|
|
|
|
|
...panelDetail,
|
|
|
|
|
|
originalImageData: result.originalImageData,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 重新触发液化面板显示事件,这次包含原始图像数据
|
|
|
|
|
|
document.dispatchEvent(
|
|
|
|
|
|
new CustomEvent("showLiquifyPanel", {
|
|
|
|
|
|
detail: updatedDetail,
|
|
|
|
|
|
})
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch((err) => {
|
|
|
|
|
|
console.error("准备液化环境失败:", err);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (checkResult.needsRasterization) {
|
|
|
|
|
|
// 需要栅格化 (多个对象或组)
|
2025-06-18 11:05:23 +08:00
|
|
|
|
// 使用Modal询问用户是否要栅格化
|
|
|
|
|
|
this._showRasterizeConfirmModal(checkResult.isGroup, activeLayerId);
|
|
|
|
|
|
return; // 等待用户确认,不继续执行
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 总是触发液化面板显示事件,不论图层状态如何
|
|
|
|
|
|
document.dispatchEvent(
|
|
|
|
|
|
new CustomEvent("showLiquifyPanel", {
|
|
|
|
|
|
detail: panelDetail,
|
|
|
|
|
|
})
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-06-18 11:05:23 +08:00
|
|
|
|
* 显示栅格化确认Modal对话框
|
|
|
|
|
|
* @param {Boolean} isGroup 是否为组对象
|
2025-06-09 10:25:54 +08:00
|
|
|
|
* @param {String} layerId 图层ID
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
2025-06-18 11:05:23 +08:00
|
|
|
|
_showRasterizeConfirmModal(isGroup, layerId) {
|
|
|
|
|
|
const title = "栅格化图层";
|
|
|
|
|
|
const content = "需要先栅格化才能进行液化操作,是否立即栅格化?";
|
|
|
|
|
|
|
|
|
|
|
|
Modal.confirm({
|
|
|
|
|
|
title,
|
|
|
|
|
|
content,
|
|
|
|
|
|
okText: "确定栅格化",
|
|
|
|
|
|
cancelText: "取消",
|
|
|
|
|
|
centered: true,
|
|
|
|
|
|
icon: h("span", { style: "color: #faad14;" }, "⚠️"),
|
|
|
|
|
|
onOk: () => {
|
|
|
|
|
|
// 用户确认栅格化,执行栅格化操作
|
2025-06-09 10:25:54 +08:00
|
|
|
|
this._rasterizeLayerForLiquify(layerId);
|
2025-06-18 11:05:23 +08:00
|
|
|
|
},
|
|
|
|
|
|
onCancel: () => {
|
|
|
|
|
|
console.log("用户取消了栅格化操作");
|
|
|
|
|
|
// 用户取消,触发液化面板显示事件但不能液化
|
|
|
|
|
|
document.dispatchEvent(
|
|
|
|
|
|
new CustomEvent("showLiquifyPanel", {
|
|
|
|
|
|
detail: {
|
|
|
|
|
|
activeLayerId: layerId,
|
|
|
|
|
|
layerStatus: { needsRasterization: true, isGroup },
|
|
|
|
|
|
canLiquify: false,
|
|
|
|
|
|
targetObject: null,
|
|
|
|
|
|
originalImageData: null,
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 栅格化图层用于液化操作
|
|
|
|
|
|
* @param {String} layerId 图层ID
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
async _rasterizeLayerForLiquify(layerId) {
|
|
|
|
|
|
if (!this.commandManager || !this.layerManager) return;
|
2025-07-24 21:45:47 +08:00
|
|
|
|
// 显示加载Modal
|
|
|
|
|
|
const loadingModal = Modal.info({
|
|
|
|
|
|
title: "正在栅格化",
|
|
|
|
|
|
content: "正在栅格化图层,请稍候...",
|
|
|
|
|
|
okButtonProps: { style: { display: "none" } },
|
|
|
|
|
|
centered: true,
|
|
|
|
|
|
closable: false,
|
|
|
|
|
|
maskClosable: false,
|
|
|
|
|
|
});
|
2025-06-09 10:25:54 +08:00
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 创建栅格化命令
|
2025-06-18 11:05:23 +08:00
|
|
|
|
const rasterizeCommand = new RasterizeLayerCommand({
|
2025-06-09 10:25:54 +08:00
|
|
|
|
canvas: this.canvas,
|
|
|
|
|
|
layerManager: this.layerManager,
|
|
|
|
|
|
layerId: layerId,
|
2025-06-18 11:05:23 +08:00
|
|
|
|
layers: this.layerManager.layers,
|
|
|
|
|
|
activeLayerId: this.layerManager.activeLayerId,
|
2025-06-09 10:25:54 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 执行命令
|
|
|
|
|
|
const result = await this.commandManager.execute(rasterizeCommand);
|
|
|
|
|
|
|
|
|
|
|
|
if (result) {
|
|
|
|
|
|
// 栅格化成功,启动液化
|
2025-06-18 11:05:23 +08:00
|
|
|
|
message.success("图层已成功栅格化,可以进行液化操作");
|
2025-07-24 21:49:29 +08:00
|
|
|
|
this._startLiquify(result);
|
|
|
|
|
|
|
|
|
|
|
|
this.setTool(OperationType.LIQUIFY); // 切换到液化工具
|
2025-06-18 11:05:23 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// 栅格化失败
|
|
|
|
|
|
Modal.error({
|
|
|
|
|
|
title: "栅格化失败",
|
|
|
|
|
|
content: "栅格化失败,无法进行液化操作",
|
|
|
|
|
|
okText: "确定",
|
|
|
|
|
|
centered: true,
|
|
|
|
|
|
});
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("栅格化图层失败:", error);
|
2025-07-24 21:45:47 +08:00
|
|
|
|
|
2025-06-18 11:05:23 +08:00
|
|
|
|
Modal.error({
|
|
|
|
|
|
title: "栅格化错误",
|
|
|
|
|
|
content: `栅格化失败:${error.message}`,
|
|
|
|
|
|
okText: "确定",
|
|
|
|
|
|
centered: true,
|
|
|
|
|
|
});
|
2025-07-24 21:45:47 +08:00
|
|
|
|
} finally {
|
|
|
|
|
|
// 关闭加载Modal
|
|
|
|
|
|
loadingModal.destroy();
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 开始液化操作
|
|
|
|
|
|
* @param {String} layerId 图层ID
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
async _startLiquify(layerId) {
|
|
|
|
|
|
// 获取图层信息
|
2025-07-24 21:37:21 +08:00
|
|
|
|
|
|
|
|
|
|
// const layer = this.layerManager.getLayerById(layerId);
|
|
|
|
|
|
const { layer } = findLayerRecursively(
|
|
|
|
|
|
this.layerManager.layers?.value ?? this.layerManager.layers,
|
|
|
|
|
|
layerId
|
|
|
|
|
|
);
|
2025-06-09 10:25:54 +08:00
|
|
|
|
if (!layer) {
|
2025-06-18 11:05:23 +08:00
|
|
|
|
Modal.error({
|
|
|
|
|
|
title: "图层错误",
|
|
|
|
|
|
content: "图层不存在",
|
|
|
|
|
|
okText: "确定",
|
|
|
|
|
|
centered: true,
|
|
|
|
|
|
});
|
2025-06-09 10:25:54 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查图层是否为空
|
|
|
|
|
|
let targetObject = null;
|
|
|
|
|
|
if (layer.isBackground) {
|
|
|
|
|
|
// 背景图层使用 fabricObject (单数)
|
|
|
|
|
|
if (!layer.fabricObject) {
|
2025-06-18 11:05:23 +08:00
|
|
|
|
Modal.warning({
|
|
|
|
|
|
title: "背景图层为空",
|
|
|
|
|
|
content: "背景图层为空,无法进行液化操作",
|
|
|
|
|
|
okText: "确定",
|
|
|
|
|
|
centered: true,
|
|
|
|
|
|
});
|
2025-06-09 10:25:54 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
targetObject = layer.fabricObject;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 普通图层使用 fabricObjects (复数)
|
|
|
|
|
|
if (!layer.fabricObjects || layer.fabricObjects.length === 0) {
|
2025-06-18 11:05:23 +08:00
|
|
|
|
Modal.warning({
|
|
|
|
|
|
title: "图层为空",
|
|
|
|
|
|
content: "图层为空,无法进行液化操作",
|
|
|
|
|
|
okText: "确定",
|
|
|
|
|
|
centered: true,
|
|
|
|
|
|
});
|
2025-06-09 10:25:54 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
targetObject = layer.fabricObjects[0];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 确保liquifyManager可用
|
|
|
|
|
|
const liquifyManager = this.canvasManager?.liquifyManager;
|
|
|
|
|
|
if (!liquifyManager) {
|
2025-06-18 11:05:23 +08:00
|
|
|
|
Modal.error({
|
|
|
|
|
|
title: "液化管理器错误",
|
|
|
|
|
|
content: "液化管理器未初始化",
|
|
|
|
|
|
okText: "确定",
|
|
|
|
|
|
centered: true,
|
|
|
|
|
|
});
|
2025-06-09 10:25:54 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
2025-06-18 11:05:23 +08:00
|
|
|
|
// 显示准备中的Modal
|
|
|
|
|
|
const preparingModal = Modal.info({
|
|
|
|
|
|
title: "准备液化环境",
|
|
|
|
|
|
content: "正在准备液化环境,请稍候...",
|
|
|
|
|
|
okButtonProps: { style: { display: "none" } },
|
|
|
|
|
|
centered: true,
|
|
|
|
|
|
closable: false,
|
|
|
|
|
|
maskClosable: false,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-06-09 10:25:54 +08:00
|
|
|
|
// 准备液化环境
|
|
|
|
|
|
liquifyManager.initialize({
|
|
|
|
|
|
canvas: this.canvas,
|
|
|
|
|
|
layerManager: this.layerManager,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 准备液化操作,获取原始图像数据
|
2025-07-21 21:28:42 +08:00
|
|
|
|
const prepareResult = await liquifyManager.prepareForLiquify(
|
|
|
|
|
|
targetObject
|
|
|
|
|
|
);
|
2025-06-09 10:25:54 +08:00
|
|
|
|
|
|
|
|
|
|
// 创建和初始化命令
|
|
|
|
|
|
const initCommand = new InitLiquifyToolCommand({
|
|
|
|
|
|
canvas: this.canvas,
|
|
|
|
|
|
layerManager: this.layerManager,
|
|
|
|
|
|
liquifyManager,
|
|
|
|
|
|
toolManager: this,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 执行初始化命令
|
|
|
|
|
|
await this.commandManager.execute(initCommand);
|
|
|
|
|
|
|
2025-06-18 11:05:23 +08:00
|
|
|
|
// 关闭准备Modal
|
|
|
|
|
|
preparingModal.destroy();
|
|
|
|
|
|
|
2025-06-09 10:25:54 +08:00
|
|
|
|
// 触发液化面板显示事件
|
|
|
|
|
|
document.dispatchEvent(
|
|
|
|
|
|
new CustomEvent("showLiquifyPanel", {
|
|
|
|
|
|
detail: {
|
|
|
|
|
|
targetObject,
|
|
|
|
|
|
targetLayerId: layerId,
|
|
|
|
|
|
originalImageData: prepareResult.originalImageData,
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("启动液化工具失败:", error);
|
2025-06-18 11:05:23 +08:00
|
|
|
|
Modal.error({
|
|
|
|
|
|
title: "液化工具启动失败",
|
|
|
|
|
|
content: `启动液化工具失败:${error.message}`,
|
|
|
|
|
|
okText: "确定",
|
|
|
|
|
|
centered: true,
|
|
|
|
|
|
});
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 触发文件上传操作
|
|
|
|
|
|
*/
|
|
|
|
|
|
openFile() {
|
|
|
|
|
|
this.onFileSelected?.();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置文件选择回调
|
|
|
|
|
|
* @param {Function} callback 回调函数
|
|
|
|
|
|
*/
|
|
|
|
|
|
setFileUploadHandler(callback) {
|
|
|
|
|
|
this.onFileSelected = callback;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新笔刷大小
|
|
|
|
|
|
* @param {Number} size 笔刷大小
|
|
|
|
|
|
*/
|
|
|
|
|
|
updateBrushSize(size) {
|
|
|
|
|
|
if (!this.canvas || !this.brushManager) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 更新BrushStore
|
|
|
|
|
|
BrushStore.setBrushSize(size);
|
|
|
|
|
|
|
|
|
|
|
|
// 直接更新笔刷管理器
|
|
|
|
|
|
this.brushManager.setBrushSize(size);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新应用到画布
|
|
|
|
|
|
this.brushManager.updateBrush();
|
2025-06-24 01:54:37 +08:00
|
|
|
|
|
|
|
|
|
|
// 更新笔刷指示器大小
|
|
|
|
|
|
this.updateBrushIndicatorSize(size);
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建文字对象并添加到画布
|
|
|
|
|
|
* @param {Number} x 文本位置x坐标
|
|
|
|
|
|
* @param {Number} y 文本位置y坐标
|
|
|
|
|
|
* @param {Object} options 文本选项
|
|
|
|
|
|
*/
|
2025-06-22 13:52:28 +08:00
|
|
|
|
async createText(x, y, options = {}) {
|
|
|
|
|
|
// 使用命令模式创建文本
|
2025-06-09 10:25:54 +08:00
|
|
|
|
if (!this.canvas || !this.layerManager) return null;
|
2025-06-22 13:52:28 +08:00
|
|
|
|
if (this.commandManager) {
|
|
|
|
|
|
const command = new CreateTextCommand({
|
|
|
|
|
|
canvas: this.canvas,
|
|
|
|
|
|
layerManager: this.layerManager,
|
|
|
|
|
|
x,
|
|
|
|
|
|
y,
|
|
|
|
|
|
textOptions: options,
|
|
|
|
|
|
});
|
|
|
|
|
|
// 执行命令
|
|
|
|
|
|
return await this.commandManager.execute(command);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 如果没有命令管理器,直接调用原有方法(兼容性)
|
|
|
|
|
|
return await this._createTextDirect(x, y, options);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-09 10:25:54 +08:00
|
|
|
|
|
2025-06-22 13:52:28 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 直接创建文本的方法(用于向后兼容)
|
|
|
|
|
|
* @param {Number} x 文本位置x坐标
|
|
|
|
|
|
* @param {Number} y 文本位置y坐标
|
|
|
|
|
|
* @param {Object} options 文本选项
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
_createTextDirect(x, y, options = {}) {
|
2025-06-09 10:25:54 +08:00
|
|
|
|
// 默认文本属性
|
|
|
|
|
|
const defaultOptions = {
|
|
|
|
|
|
text: "双击编辑文本",
|
|
|
|
|
|
fontFamily: "Arial",
|
|
|
|
|
|
fontSize: 24,
|
|
|
|
|
|
fontWeight: "normal",
|
|
|
|
|
|
fontStyle: "normal",
|
|
|
|
|
|
textAlign: "left",
|
|
|
|
|
|
fill: "#000000",
|
|
|
|
|
|
opacity: 1,
|
|
|
|
|
|
underline: false,
|
|
|
|
|
|
overline: false,
|
|
|
|
|
|
linethrough: false,
|
|
|
|
|
|
textBackgroundColor: "transparent",
|
|
|
|
|
|
lineHeight: 1.16,
|
|
|
|
|
|
charSpacing: 0,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 合并默认选项和用户选项
|
|
|
|
|
|
const textOptions = { ...defaultOptions, ...options, left: x, top: y };
|
|
|
|
|
|
|
|
|
|
|
|
// 创建文本对象
|
|
|
|
|
|
const textObj = new fabric.IText(textOptions.text, {
|
|
|
|
|
|
...textOptions,
|
|
|
|
|
|
originX: "center",
|
|
|
|
|
|
originY: "center",
|
|
|
|
|
|
id: options.id || this.generateId(), // 生成唯一ID
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 创建文本图层并通过LayerManager添加到画布
|
|
|
|
|
|
this.layerManager.createTextLayerWithObject(textObj, textOptions);
|
|
|
|
|
|
|
|
|
|
|
|
return textObj;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 生成唯一ID
|
|
|
|
|
|
* @returns {String} 唯一ID
|
|
|
|
|
|
*/
|
|
|
|
|
|
generateId() {
|
|
|
|
|
|
return "text_" + Date.now() + "_" + Math.floor(Math.random() * 1000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新笔刷颜色
|
|
|
|
|
|
* @param {String} color 颜色值,如 "#ff0000"
|
|
|
|
|
|
*/
|
|
|
|
|
|
updateBrushColor(color) {
|
|
|
|
|
|
if (!this.canvas || !this.brushManager) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 更新BrushStore
|
|
|
|
|
|
BrushStore.setBrushColor(color);
|
|
|
|
|
|
|
|
|
|
|
|
// 直接更新笔刷管理器
|
|
|
|
|
|
this.brushManager.setBrushColor(color);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新应用到画布
|
|
|
|
|
|
this.brushManager.updateBrush();
|
2025-06-24 01:54:37 +08:00
|
|
|
|
|
|
|
|
|
|
// 更新笔刷指示器颜色
|
|
|
|
|
|
this.updateBrushIndicatorColor(color);
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新笔刷透明度
|
|
|
|
|
|
* @param {Number} opacity 透明度值,范围 0-1
|
|
|
|
|
|
*/
|
|
|
|
|
|
updateBrushOpacity(opacity) {
|
|
|
|
|
|
if (!this.canvas || !this.brushManager) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 更新BrushStore
|
|
|
|
|
|
BrushStore.setBrushOpacity(opacity);
|
|
|
|
|
|
|
|
|
|
|
|
// 直接更新笔刷管理器
|
|
|
|
|
|
this.brushManager.setBrushOpacity(opacity);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新应用到画布
|
|
|
|
|
|
this.brushManager.updateBrush();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置双击文本的事件监听
|
|
|
|
|
|
* 当用户双击文本对象时,显示文本编辑弹窗
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupTextEditingEvents() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 如果已有监听器,先移除以避免重复
|
|
|
|
|
|
if (this._textEditHandler) {
|
|
|
|
|
|
this.canvas.off("mouse:dblclick", this._textEditHandler);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建双击事件处理函数
|
|
|
|
|
|
this._textEditHandler = (e) => {
|
|
|
|
|
|
const target = e.target;
|
|
|
|
|
|
if (
|
|
|
|
|
|
target &&
|
2025-07-21 21:28:42 +08:00
|
|
|
|
(target.type === "text" ||
|
|
|
|
|
|
target.type === "i-text" ||
|
|
|
|
|
|
target.type === "textbox")
|
2025-06-09 10:25:54 +08:00
|
|
|
|
) {
|
|
|
|
|
|
// 获取对应的图层
|
2025-07-24 21:37:21 +08:00
|
|
|
|
// const layer = this.layerManager.getLayerById(target.layerId);
|
|
|
|
|
|
const { layer } = findLayerRecursively(
|
|
|
|
|
|
this.layerManager.layers?.value ?? this.layerManager.layers,
|
|
|
|
|
|
target.layerId
|
|
|
|
|
|
);
|
2025-06-09 10:25:54 +08:00
|
|
|
|
if (layer) {
|
|
|
|
|
|
// 显示文本编辑面板
|
|
|
|
|
|
this.showTextEditor(target, layer);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 添加双击事件监听
|
|
|
|
|
|
this.canvas.on("mouse:dblclick", this._textEditHandler);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 显示文本编辑面板
|
|
|
|
|
|
* @param {Object} textObject 文本对象
|
|
|
|
|
|
* @param {Object} layer 图层对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
showTextEditor(textObject, layer) {
|
|
|
|
|
|
// 这个方法将在TextEditorPanel组件实现后调用
|
|
|
|
|
|
console.log("显示文本编辑面板", textObject, layer);
|
|
|
|
|
|
// 将发出一个事件,让Vue组件捕获并显示编辑面板
|
|
|
|
|
|
document.dispatchEvent(
|
|
|
|
|
|
new CustomEvent("showTextEditor", {
|
|
|
|
|
|
detail: {
|
|
|
|
|
|
textObject,
|
|
|
|
|
|
layer,
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 清理资源
|
|
|
|
|
|
*/
|
|
|
|
|
|
dispose() {
|
|
|
|
|
|
if (this.brushManager) {
|
|
|
|
|
|
this.brushManager.dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-24 01:54:37 +08:00
|
|
|
|
// 清理笔刷指示器
|
|
|
|
|
|
if (this.brushIndicator) {
|
|
|
|
|
|
this.brushIndicator.dispose();
|
|
|
|
|
|
this.brushIndicator = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-09 10:25:54 +08:00
|
|
|
|
// 移除文本编辑相关事件监听器
|
|
|
|
|
|
if (this.canvas) {
|
|
|
|
|
|
this.canvas.off("mouse:dblclick", this._textEditHandler);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this._textEditHandler = null;
|
|
|
|
|
|
|
|
|
|
|
|
this.observers = []; // 清空观察者
|
|
|
|
|
|
this.canvas = null;
|
|
|
|
|
|
this.commandManager = null;
|
|
|
|
|
|
this.onFileSelected = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置文本工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupTextTool() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.isDrawingMode = false;
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
|
|
|
|
|
|
console.log("文本工具已激活");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置红色笔刷工具(红绿图模式专用)
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupRedBrushTool() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.isDrawingMode = true;
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
|
2025-06-24 01:54:37 +08:00
|
|
|
|
const redColor = "#FF0000";
|
|
|
|
|
|
|
2025-06-09 10:25:54 +08:00
|
|
|
|
// 确保有笔刷管理器
|
|
|
|
|
|
if (this.brushManager) {
|
|
|
|
|
|
// 设置红色笔刷
|
2025-06-24 01:54:37 +08:00
|
|
|
|
this.brushManager.setBrushColor(redColor); // 纯红色
|
2025-06-09 10:25:54 +08:00
|
|
|
|
this.brushManager.setBrushOpacity(1.0); // 完全不透明
|
|
|
|
|
|
this.brushManager.setBrushType("pencil"); // 铅笔类型
|
|
|
|
|
|
|
|
|
|
|
|
// 更新笔刷大小(使用当前大小)
|
|
|
|
|
|
if (BrushStore && BrushStore.state.size) {
|
|
|
|
|
|
this.brushManager.setBrushSize(BrushStore.state.size);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新应用到画布
|
|
|
|
|
|
this.brushManager.updateBrush();
|
|
|
|
|
|
}
|
2025-06-24 01:54:37 +08:00
|
|
|
|
|
|
|
|
|
|
// 启用笔刷指示器并设置红色
|
|
|
|
|
|
this._enableBrushIndicator(redColor);
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置绿色笔刷工具(红绿图模式专用)
|
|
|
|
|
|
*/
|
|
|
|
|
|
setupGreenBrushTool() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas.isDrawingMode = true;
|
|
|
|
|
|
this.canvas.selection = false;
|
|
|
|
|
|
|
2025-06-24 01:54:37 +08:00
|
|
|
|
const greenColor = "#00FF00";
|
|
|
|
|
|
|
2025-06-09 10:25:54 +08:00
|
|
|
|
// 确保有笔刷管理器
|
|
|
|
|
|
if (this.brushManager) {
|
|
|
|
|
|
// 设置绿色笔刷
|
2025-06-24 01:54:37 +08:00
|
|
|
|
this.brushManager.setBrushColor(greenColor); // 纯绿色
|
2025-06-09 10:25:54 +08:00
|
|
|
|
this.brushManager.setBrushOpacity(1.0); // 完全不透明
|
|
|
|
|
|
this.brushManager.setBrushType("pencil"); // 铅笔类型
|
|
|
|
|
|
|
|
|
|
|
|
// 更新笔刷大小(使用当前大小)
|
|
|
|
|
|
if (BrushStore && BrushStore.state.size) {
|
|
|
|
|
|
this.brushManager.setBrushSize(BrushStore.state.size);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新应用到画布
|
|
|
|
|
|
this.brushManager.updateBrush();
|
|
|
|
|
|
}
|
2025-06-24 01:54:37 +08:00
|
|
|
|
|
|
|
|
|
|
// 启用笔刷指示器并设置绿色
|
|
|
|
|
|
this._enableBrushIndicator(greenColor);
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 进入红绿图模式
|
|
|
|
|
|
* @param {Object} redGreenModeManager 红绿图模式管理器实例
|
|
|
|
|
|
*/
|
|
|
|
|
|
enterRedGreenMode(redGreenModeManager) {
|
|
|
|
|
|
this.isRedGreenMode = true;
|
|
|
|
|
|
this.redGreenModeManager = redGreenModeManager;
|
|
|
|
|
|
|
|
|
|
|
|
// 切换到红色笔刷工具作为默认工具
|
|
|
|
|
|
this.setTool(OperationType.RED_BRUSH);
|
|
|
|
|
|
|
|
|
|
|
|
console.log("工具管理器已进入红绿图模式");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 退出红绿图模式
|
|
|
|
|
|
*/
|
|
|
|
|
|
exitRedGreenMode() {
|
|
|
|
|
|
this.isRedGreenMode = false;
|
|
|
|
|
|
this.redGreenModeManager = null;
|
|
|
|
|
|
|
|
|
|
|
|
// 切换回选择工具
|
|
|
|
|
|
this.setTool(OperationType.SELECT);
|
|
|
|
|
|
|
|
|
|
|
|
console.log("工具管理器已退出红绿图模式");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查工具是否在红绿图模式下可用
|
|
|
|
|
|
* @param {String} toolId 工具ID
|
|
|
|
|
|
* @returns {Boolean} 是否可用
|
|
|
|
|
|
*/
|
|
|
|
|
|
isToolAvailableInRedGreenMode(toolId) {
|
|
|
|
|
|
if (!this.isRedGreenMode) return true;
|
|
|
|
|
|
|
|
|
|
|
|
const tool = this.tools[toolId];
|
|
|
|
|
|
return tool && tool.allowedInRedGreen === true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取红绿图模式下可用的工具列表
|
|
|
|
|
|
* @returns {Array} 工具列表
|
|
|
|
|
|
*/
|
|
|
|
|
|
getRedGreenModeTools() {
|
|
|
|
|
|
return Object.keys(this.tools)
|
|
|
|
|
|
.filter((toolId) => this.tools[toolId].allowedInRedGreen === true)
|
|
|
|
|
|
.map((toolId) => ({
|
|
|
|
|
|
id: toolId,
|
|
|
|
|
|
...this.tools[toolId],
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查是否为红绿图模式专用工具
|
|
|
|
|
|
* @param {String} toolId 工具ID
|
|
|
|
|
|
* @returns {Boolean} 是否为红绿图模式专用工具
|
|
|
|
|
|
*/
|
|
|
|
|
|
isRedGreenOnlyTool(toolId) {
|
|
|
|
|
|
const tool = this.tools[toolId];
|
|
|
|
|
|
return tool && tool.redGreenOnly === true;
|
|
|
|
|
|
}
|
2025-06-24 01:54:37 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 启用笔刷指示器
|
|
|
|
|
|
* @param {String} color 笔刷颜色(可选)
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
_enableBrushIndicator(color) {
|
|
|
|
|
|
if (!this.brushIndicator) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前笔刷大小
|
|
|
|
|
|
const brushSize = this._getCurrentBrushSize();
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前笔刷颜色
|
|
|
|
|
|
const brushColor = color || this._getCurrentBrushColor();
|
|
|
|
|
|
|
|
|
|
|
|
// 启用指示器
|
|
|
|
|
|
this.brushIndicator.enable(brushSize);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新指示器颜色
|
|
|
|
|
|
this.brushIndicator.updateColor(brushColor);
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`笔刷指示器已启用,大小: ${brushSize}, 颜色: ${brushColor}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 禁用笔刷指示器
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
_disableBrushIndicator() {
|
|
|
|
|
|
if (!this.brushIndicator) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.brushIndicator.disable();
|
|
|
|
|
|
console.log("笔刷指示器已禁用");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新笔刷指示器大小
|
|
|
|
|
|
* @param {Number} size 新的笔刷大小
|
|
|
|
|
|
*/
|
|
|
|
|
|
updateBrushIndicatorSize(size) {
|
|
|
|
|
|
if (!this.brushIndicator) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.brushIndicator.updateSize(size);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新笔刷指示器颜色
|
|
|
|
|
|
* @param {String} color 新的笔刷颜色
|
|
|
|
|
|
*/
|
|
|
|
|
|
updateBrushIndicatorColor(color) {
|
|
|
|
|
|
if (!this.brushIndicator) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.brushIndicator.updateColor(color);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取当前笔刷大小
|
|
|
|
|
|
* @private
|
|
|
|
|
|
* @returns {Number} 当前笔刷大小
|
|
|
|
|
|
*/
|
|
|
|
|
|
_getCurrentBrushSize() {
|
|
|
|
|
|
// 优先从 BrushStore 获取
|
|
|
|
|
|
if (BrushStore && BrushStore.state.size) {
|
|
|
|
|
|
return BrushStore.state.size;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 从笔刷管理器获取
|
|
|
|
|
|
if (this.brushManager && this.brushManager.getBrushSize) {
|
|
|
|
|
|
return this.brushManager.getBrushSize();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 从画布笔刷获取
|
2025-07-21 21:28:42 +08:00
|
|
|
|
if (
|
|
|
|
|
|
this.canvas &&
|
|
|
|
|
|
this.canvas.freeDrawingBrush &&
|
|
|
|
|
|
this.canvas.freeDrawingBrush.width
|
|
|
|
|
|
) {
|
2025-06-24 01:54:37 +08:00
|
|
|
|
return this.canvas.freeDrawingBrush.width;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 默认值
|
|
|
|
|
|
return 10;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取当前笔刷颜色
|
|
|
|
|
|
* @private
|
|
|
|
|
|
* @returns {String} 当前笔刷颜色
|
|
|
|
|
|
*/
|
|
|
|
|
|
_getCurrentBrushColor() {
|
|
|
|
|
|
// 优先从 BrushStore 获取
|
|
|
|
|
|
if (BrushStore && BrushStore.state.color) {
|
|
|
|
|
|
return BrushStore.state.color;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 从笔刷管理器获取
|
|
|
|
|
|
if (this.brushManager && this.brushManager.getBrushColor) {
|
|
|
|
|
|
return this.brushManager.getBrushColor();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 从画布笔刷获取
|
2025-07-21 21:28:42 +08:00
|
|
|
|
if (
|
|
|
|
|
|
this.canvas &&
|
|
|
|
|
|
this.canvas.freeDrawingBrush &&
|
|
|
|
|
|
this.canvas.freeDrawingBrush.color
|
|
|
|
|
|
) {
|
2025-06-24 01:54:37 +08:00
|
|
|
|
return this.canvas.freeDrawingBrush.color;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 默认值
|
|
|
|
|
|
return "#000000";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查当前工具是否需要显示笔刷指示器
|
|
|
|
|
|
* @param {String} toolId 工具ID(可选,不传则使用当前工具)
|
|
|
|
|
|
* @returns {Boolean} 是否需要显示
|
|
|
|
|
|
*/
|
|
|
|
|
|
_shouldShowBrushIndicator(toolId) {
|
|
|
|
|
|
const currentTool = toolId || this.activeTool.value;
|
|
|
|
|
|
|
|
|
|
|
|
// 画笔相关工具需要显示指示器
|
|
|
|
|
|
const brushTools = [
|
|
|
|
|
|
OperationType.DRAW,
|
|
|
|
|
|
OperationType.ERASER,
|
|
|
|
|
|
OperationType.RED_BRUSH,
|
|
|
|
|
|
OperationType.GREEN_BRUSH,
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
return brushTools.includes(currentTool);
|
|
|
|
|
|
}
|
2025-06-09 10:25:54 +08:00
|
|
|
|
}
|