Files
aida_front/src/component/Canvas/CanvasEditor/commands/TextCommands.js
2025-11-11 17:35:00 +08:00

619 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { generateId, optimizeCanvasRendering, getLayerObjectsZIndex } from "../utils/helper";
import { createLayer, LayerType, OperationType } from "../utils/layerHelper";
import { Command } from "./Command";
import i18n from "@/lang/index.ts";
const { t } = i18n.global;
/**
* 文本内容命令
* 用于更改文本图层的文本内容
*/
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;
}
}
/**
* 创建文本命令
* 用于创建文本对象和图层的组合操作
*/
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;
// 生成唯一ID
this.textId = options?.textId || generateId("text_");
this.layerId = options?.layerId || generateId("text_layer_");
// 生成的对象和图层信息
this.textObject = null;
this.oldActiveLayerId = null;
// 默认文本属性
this.defaultOptions = {
text: options.text || t('Canvas.DoubleClickText'),
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');
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?.layerSort?.rearrangeObjects();
// 更新对象交互性
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,
};
}
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);
}
});
// 智能移除创建的图层
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}`);
}
}
}
}