Files
aida_front/src/component/Canvas/CanvasEditor/commands/TextCommands.js

619 lines
16 KiB
JavaScript
Raw Normal View History

import { generateId, optimizeCanvasRendering, getLayerObjectsZIndex } from "../utils/helper";
2025-06-22 13:52:28 +08:00
import { createLayer, LayerType, OperationType } from "../utils/layerHelper";
2025-06-09 10:25:54 +08:00
import { Command } from "./Command";
2025-09-24 16:26:40 +08:00
import i18n from "@/lang/index.ts";
const { t } = i18n.global;
2025-06-09 10:25:54 +08:00
/**
* 文本内容命令
* 用于更改文本图层的文本内容
*/
export class TextContentCommand extends Command {
constructor(options) {
super({
name: "修改文本内容",
description: "修改文本图层的文本内容",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.newText = options.newText;
this.oldText = this.textObject.text;
}
execute() {
this.textObject.set("text", this.newText);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set("text", this.oldText);
this.canvas.renderAll();
return true;
}
}
/**
* 文本字体命令
* 用于更改文本图层的字体
*/
export class TextFontCommand extends Command {
constructor(options) {
super({
name: "修改文本字体",
description: "修改文本图层的字体",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.newFont = options.newFont;
this.oldFont = this.textObject.fontFamily;
}
execute() {
this.textObject.set("fontFamily", this.newFont);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set("fontFamily", this.oldFont);
this.canvas.renderAll();
return true;
}
}
/**
* 文本尺寸命令
* 用于更改文本图层的字体大小
*/
export class TextSizeCommand extends Command {
constructor(options) {
super({
name: "修改文本尺寸",
description: "修改文本图层的字体大小",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.newSize = options.newSize;
this.oldSize = this.textObject.fontSize;
}
execute() {
this.textObject.set("fontSize", this.newSize);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set("fontSize", this.oldSize);
this.canvas.renderAll();
return true;
}
}
/**
* 文本颜色命令
* 用于更改文本图层的颜色
*/
export class TextColorCommand extends Command {
constructor(options) {
super({
name: "修改文本颜色",
description: "修改文本图层的颜色",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.newColor = options.newColor;
this.oldColor = this.textObject.fill;
}
execute() {
this.textObject.set("fill", this.newColor);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set("fill", this.oldColor);
this.canvas.renderAll();
return true;
}
}
/**
* 文本对齐方式命令
* 用于更改文本图层的对齐方式
*/
export class TextAlignCommand extends Command {
constructor(options) {
super({
name: "修改文本对齐",
description: "修改文本图层的对齐方式",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.newAlign = options.newAlign;
this.oldAlign = this.textObject.textAlign;
}
execute() {
this.textObject.set("textAlign", this.newAlign);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set("textAlign", this.oldAlign);
this.canvas.renderAll();
return true;
}
}
/**
* 文本样式命令
* 用于更改文本图层的样式(粗体斜体下划线等)
*/
export class TextStyleCommand extends Command {
constructor(options) {
super({
name: "修改文本样式",
description: "修改文本图层的样式",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.property = options.property; // 'fontWeight', 'fontStyle', 'underline', 'linethrough', 'overline'
this.newValue = options.newValue;
this.oldValue = this.textObject[this.property];
}
execute() {
this.textObject.set(this.property, this.newValue);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set(this.property, this.oldValue);
this.canvas.renderAll();
return true;
}
}
/**
* 文本间距命令
* 用于更改文本图层的字符间距或行高
*/
export class TextSpacingCommand extends Command {
constructor(options) {
super({
name: "修改文本间距",
description: "修改文本图层的字符间距或行高",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.property = options.property; // 'charSpacing' 或 'lineHeight'
this.newValue = options.newValue;
this.oldValue = this.textObject[this.property];
}
execute() {
this.textObject.set(this.property, this.newValue);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set(this.property, this.oldValue);
this.canvas.renderAll();
return true;
}
}
/**
* 文本背景颜色命令
* 用于更改文本图层的背景颜色
*/
export class TextBackgroundCommand extends Command {
constructor(options) {
super({
name: "修改文本背景",
description: "修改文本图层的背景颜色",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.newColor = options.newColor;
this.oldColor = this.textObject.textBackgroundColor;
}
execute() {
this.textObject.set("textBackgroundColor", this.newColor);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set("textBackgroundColor", this.oldColor);
this.canvas.renderAll();
return true;
}
}
/**
* 文本透明度命令
* 用于更改文本图层的透明度
*/
export class TextOpacityCommand extends Command {
constructor(options) {
super({
name: "修改文本透明度",
description: "修改文本图层的透明度",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.newOpacity = options.newOpacity;
this.oldOpacity = this.textObject.opacity;
}
execute() {
this.textObject.set("opacity", this.newOpacity);
this.canvas.renderAll();
return true;
}
undo() {
this.textObject.set("opacity", this.oldOpacity);
this.canvas.renderAll();
return true;
}
}
/**
* 组合文本编辑命令
* 用于一次性应用多个文本属性更改
*/
export class CompositeTextCommand extends Command {
constructor(options) {
super({
name: "组合文本编辑",
description: "组合多个文本编辑操作",
});
this.canvas = options.canvas;
this.textObject = options.textObject;
this.changes = options.changes; // {property: newValue} 形式的对象
this.oldValues = {};
// 保存所有属性的旧值
for (const property in this.changes) {
if (this.textObject[property] !== undefined) {
this.oldValues[property] = this.textObject[property];
}
}
}
execute() {
for (const property in this.changes) {
this.textObject.set(property, this.changes[property]);
}
this.canvas.renderAll();
return true;
}
undo() {
for (const property in this.oldValues) {
this.textObject.set(property, this.oldValues[property]);
}
this.canvas.renderAll();
return true;
}
}
2025-06-22 13:52:28 +08:00
/**
* 创建文本命令
* 用于创建文本对象和图层的组合操作
*/
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 || {};
this.options = options;
2025-06-22 13:52:28 +08:00
// 生成唯一ID
this.textId = options?.textId || generateId("text_");
2025-06-22 13:52:28 +08:00
this.layerId = options?.layerId || generateId("text_layer_");
// 生成的对象和图层信息
this.textObject = null;
this.oldActiveLayerId = null;
// 默认文本属性
this.defaultOptions = {
text: options.text || t('Canvas.DoubleClickText'),
2025-06-22 13:52:28 +08:00
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",
});
// 创建文本图层 取15
const layerName = this.options.text?.substring(0, 25) || t('Canvas.TextLayer');
2025-06-22 13:52:28 +08:00
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"]));
2025-06-22 13:52:28 +08:00
}
// 现在可以安全地设置为活动图层
this.layerManager.setActiveLayer(this.layerId);
// 重新排序图层对象
await this.layerManager?.layerSort?.rearrangeObjects();
2025-06-22 13:52:28 +08:00
// 更新对象交互性
await this.layerManager?.updateLayersObjectsInteractivity?.(false);
// 切换到选择工具
this.layerManager?.toolManager?.setTool?.(OperationType.SELECT);
});
console.log(`✅ 文本对象已创建: "${finalOptions.text}",位置: (${this.x}, ${this.y})`);
2025-06-22 13:52:28 +08:00
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})`);
2025-06-22 13:52:28 +08:00
} else {
// 当前激活图层是一级图层
// 在一级图层中,插入到激活图层之上
const activeLayerIndex = layers.findIndex((layer) => layer.id === currentActiveLayerId);
2025-06-22 13:52:28 +08:00
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,
};
}
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);
}
const layerObjects = getLayerObjectsZIndex(this.canvas, this.layerId);
layerObjects.forEach((obj) => {
if (obj.id === this.textObject?.id) {
this.canvas.remove(obj.object);
}
});
2025-06-22 13:52:28 +08:00
// 智能移除创建的图层
if (this.layerId && this.layerManager) {
this._removeLayerFromCorrectPosition();
}
// 恢复原活动图层
if (this.oldActiveLayerId && this.layerManager) {
// 检查原活动图层是否还存在
const originalLayer = this.layerManager.getLayerById(this.oldActiveLayerId);
2025-06-22 13:52:28 +08:00
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})`);
2025-06-22 13:52:28 +08:00
}
} else {
// 从一级图层中移除
if (positionInfo.index >= 0) {
layers.splice(positionInfo.index, 1);
console.log(`已从一级图层移除: ${this.layerId}`);
}
}
}
}