合并画布
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { generateId, optimizeCanvasRendering } from "../utils/helper";
|
||||
import { createLayer, LayerType, OperationType } from "../utils/layerHelper";
|
||||
import { Command } from "./Command";
|
||||
|
||||
/**
|
||||
@@ -302,3 +304,323 @@ export class CompositeTextCommand extends Command {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文本命令
|
||||
* 用于创建文本对象和图层的组合操作
|
||||
*/
|
||||
export class CreateTextCommand extends Command {
|
||||
constructor(options) {
|
||||
super({
|
||||
name: "创建文本",
|
||||
});
|
||||
this.canvas = options.canvas;
|
||||
this.layerManager = options.layerManager;
|
||||
this.x = options.x;
|
||||
this.y = options.y;
|
||||
this.textOptions = options.textOptions || {};
|
||||
// 生成唯一ID
|
||||
this.textId = options?.textId || this.generateId("text_");
|
||||
this.layerId = options?.layerId || generateId("text_layer_");
|
||||
|
||||
// 生成的对象和图层信息
|
||||
this.textObject = null;
|
||||
this.oldActiveLayerId = null;
|
||||
|
||||
// 默认文本属性
|
||||
this.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,
|
||||
};
|
||||
}
|
||||
|
||||
async execute() {
|
||||
if (!this.canvas || !this.layerManager) {
|
||||
console.error("Canvas或LayerManager不存在");
|
||||
return null;
|
||||
}
|
||||
// 保存当前活动图层
|
||||
this.oldActiveLayerId = this.layerManager.activeLayerId?.value;
|
||||
|
||||
// 合并默认选项和用户选项
|
||||
const finalOptions = {
|
||||
...this.defaultOptions,
|
||||
...this.textOptions,
|
||||
left: this.x,
|
||||
top: this.y,
|
||||
};
|
||||
|
||||
try {
|
||||
await optimizeCanvasRendering(this.canvas, async () => {
|
||||
// 创建文本对象
|
||||
this.textObject = new fabric.IText(finalOptions.text, {
|
||||
...finalOptions,
|
||||
originX: "center",
|
||||
originY: "center",
|
||||
});
|
||||
|
||||
// 创建文本图层
|
||||
const layerName = this.textOptions.layerName || "文本图层";
|
||||
const layer = createLayer({
|
||||
id: this.layerId,
|
||||
name: layerName,
|
||||
type: LayerType.TEXT,
|
||||
});
|
||||
|
||||
// 设置对象的图层关联
|
||||
this.textObject.set({
|
||||
id: this.textId,
|
||||
layerId: this.layerId,
|
||||
layerName: layerName,
|
||||
});
|
||||
|
||||
// 智能插入图层到合适位置
|
||||
this._insertLayerAtCorrectPosition(layer);
|
||||
|
||||
// 添加到画布
|
||||
this.canvas.add(this.textObject);
|
||||
|
||||
// 取消其他对象的选中状态
|
||||
this.canvas.discardActiveObject();
|
||||
|
||||
// 设置新创建的文本对象为活动对象
|
||||
this.canvas.setActiveObject(this.textObject);
|
||||
|
||||
// 更新图层的对象列表
|
||||
if (layer) {
|
||||
layer.fabricObjects = layer.fabricObjects || [];
|
||||
layer.fabricObjects.push(
|
||||
this.textObject.toObject(["id", "layerId", "layerName"])
|
||||
);
|
||||
}
|
||||
|
||||
// 现在可以安全地设置为活动图层
|
||||
this.layerManager.setActiveLayer(this.layerId);
|
||||
|
||||
// 更新对象交互性
|
||||
await this.layerManager?.updateLayersObjectsInteractivity?.(false);
|
||||
|
||||
// 切换到选择工具
|
||||
this.layerManager?.toolManager?.setTool?.(OperationType.SELECT);
|
||||
});
|
||||
|
||||
console.log(
|
||||
`✅ 文本对象已创建: "${finalOptions.text}",位置: (${this.x}, ${this.y})`
|
||||
);
|
||||
return this.textObject;
|
||||
} catch (error) {
|
||||
console.error("创建文本对象失败:", error);
|
||||
// 如果创建失败,需要清理已创建的资源
|
||||
await this.undo();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能插入图层到正确位置
|
||||
* 根据当前激活图层位置确定新图层插入位置
|
||||
* @param {Object} newLayer 要插入的新图层
|
||||
* @private
|
||||
*/
|
||||
_insertLayerAtCorrectPosition(newLayer) {
|
||||
const layers = this.layerManager.layers.value;
|
||||
const currentActiveLayerId = this.layerManager.activeLayerId?.value;
|
||||
|
||||
// 如果没有当前激活图层,插入到顶部(索引0)
|
||||
if (!currentActiveLayerId) {
|
||||
layers.splice(0, 0, newLayer);
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找当前激活图层的位置
|
||||
const {
|
||||
layer: activeLayer,
|
||||
parent: parentLayer,
|
||||
index: activeIndex,
|
||||
} = this._findLayerPosition(currentActiveLayerId);
|
||||
|
||||
if (!activeLayer) {
|
||||
// 没找到激活图层,插入到顶部
|
||||
layers.splice(0, 0, newLayer);
|
||||
return;
|
||||
}
|
||||
|
||||
// 确定插入位置
|
||||
let insertIndex = 0;
|
||||
|
||||
if (parentLayer) {
|
||||
// 当前激活图层是子图层
|
||||
// 在同一父图层内,插入到激活子图层之上
|
||||
insertIndex = Math.max(0, activeIndex);
|
||||
parentLayer.children = parentLayer.children || [];
|
||||
parentLayer.children.splice(insertIndex, 0, newLayer);
|
||||
|
||||
console.log(
|
||||
`新图层已插入到子图层位置: ${insertIndex} (父图层: ${parentLayer.name})`
|
||||
);
|
||||
} else {
|
||||
// 当前激活图层是一级图层
|
||||
// 在一级图层中,插入到激活图层之上
|
||||
const activeLayerIndex = layers.findIndex(
|
||||
(layer) => layer.id === currentActiveLayerId
|
||||
);
|
||||
insertIndex = Math.max(0, activeLayerIndex);
|
||||
layers.splice(insertIndex, 0, newLayer);
|
||||
|
||||
console.log(`新图层已插入到一级图层位置: ${insertIndex}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找图层位置信息
|
||||
* @param {String} layerId 图层ID
|
||||
* @returns {Object} 包含图层、父图层和索引的对象
|
||||
* @private
|
||||
*/
|
||||
_findLayerPosition(layerId) {
|
||||
const layers = this.layerManager.layers.value;
|
||||
|
||||
// 先在一级图层中查找
|
||||
for (let i = 0; i < layers.length; i++) {
|
||||
const layer = layers[i];
|
||||
|
||||
if (layer.id === layerId) {
|
||||
return {
|
||||
layer: layer,
|
||||
parent: null,
|
||||
index: i,
|
||||
};
|
||||
}
|
||||
|
||||
// 在子图层中查找
|
||||
if (layer.children && Array.isArray(layer.children)) {
|
||||
for (let j = 0; j < layer.children.length; j++) {
|
||||
const childLayer = layer.children[j];
|
||||
if (childLayer.id === layerId) {
|
||||
return {
|
||||
layer: childLayer,
|
||||
parent: layer,
|
||||
index: j,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
layer: null,
|
||||
parent: null,
|
||||
index: -1,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一ID
|
||||
* @returns {String} 唯一ID
|
||||
*/
|
||||
generateId() {
|
||||
return `text_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
||||
}
|
||||
|
||||
getInfo() {
|
||||
return {
|
||||
name: this.name,
|
||||
textId: this.textObject?.id,
|
||||
layerId: this.layerId,
|
||||
text: this.textOptions.text || this.defaultOptions.text,
|
||||
position: { x: this.x, y: this.y },
|
||||
};
|
||||
}
|
||||
|
||||
async undo() {
|
||||
try {
|
||||
// 从画布移除文本对象
|
||||
if (this.textObject && this.canvas) {
|
||||
this.canvas.remove(this.textObject);
|
||||
}
|
||||
|
||||
// 智能移除创建的图层
|
||||
if (this.layerId && this.layerManager) {
|
||||
this._removeLayerFromCorrectPosition();
|
||||
}
|
||||
|
||||
// 恢复原活动图层
|
||||
if (this.oldActiveLayerId && this.layerManager) {
|
||||
// 检查原活动图层是否还存在
|
||||
const originalLayer = this.layerManager.getLayerById(
|
||||
this.oldActiveLayerId
|
||||
);
|
||||
if (originalLayer) {
|
||||
this.layerManager.setActiveLayer(this.oldActiveLayerId);
|
||||
} else {
|
||||
// 如果原图层不存在,设置为第一个可用的普通图层
|
||||
const availableLayers = this.layerManager.layers.value.filter(
|
||||
(layer) => !layer.isBackground && !layer.isFixed && !layer.locked
|
||||
);
|
||||
if (availableLayers.length > 0) {
|
||||
this.layerManager.setActiveLayer(availableLayers[0].id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新对象交互性
|
||||
await this.layerManager.updateLayersObjectsInteractivity();
|
||||
|
||||
// 重新渲染画布
|
||||
if (this.canvas) {
|
||||
this.canvas.renderAll();
|
||||
}
|
||||
|
||||
console.log(`↩️ 文本创建操作已撤销`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("撤销文本创建操作失败:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能移除图层
|
||||
* 根据图层位置(一级图层或子图层)进行相应的移除操作
|
||||
* @private
|
||||
*/
|
||||
_removeLayerFromCorrectPosition() {
|
||||
const layers = this.layerManager.layers.value;
|
||||
|
||||
// 查找图层位置信息
|
||||
const positionInfo = this._findLayerPosition(this.layerId);
|
||||
|
||||
if (!positionInfo.layer) {
|
||||
console.warn(`要移除的图层不存在: ${this.layerId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (positionInfo.parent) {
|
||||
// 从子图层中移除
|
||||
if (positionInfo.parent.children && positionInfo.index >= 0) {
|
||||
positionInfo.parent.children.splice(positionInfo.index, 1);
|
||||
console.log(
|
||||
`已从子图层移除: ${this.layerId} (父图层: ${positionInfo.parent.name})`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 从一级图层中移除
|
||||
if (positionInfo.index >= 0) {
|
||||
layers.splice(positionInfo.index, 1);
|
||||
console.log(`已从一级图层移除: ${this.layerId}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user