229 lines
6.5 KiB
JavaScript
229 lines
6.5 KiB
JavaScript
import { findObjectById } from "../utils/helper";
|
|
import { findLayerRecursively } from "../utils/layerHelper";
|
|
import { restoreFabricObject } from "../utils/objectHelper";
|
|
import { Command } from "./Command";
|
|
|
|
/**
|
|
* 对象变换命令
|
|
* 轻量级命令,只记录对象的变换属性变化(位置、缩放、旋转)
|
|
* 不保存整个对象或画布状态,只关注变换属性
|
|
*/
|
|
export class TransformCommand extends Command {
|
|
constructor(options) {
|
|
super({
|
|
name: options.name || "对象变换",
|
|
description: options.description || "移动、缩放或旋转对象",
|
|
saveState: false, // 自己管理状态,避免递归
|
|
});
|
|
|
|
this.canvas = options.canvas;
|
|
this.objectId = options.objectId;
|
|
this.initialState = options.initialState || null;
|
|
this.finalState = options.finalState || null;
|
|
this.objectType = options.objectType || "object";
|
|
this.layerManager = options.layerManager;
|
|
this.layers = options.layers || null;
|
|
this.lastSelectLayerId = options.lastSelectLayerId || null; // 最后选择的图层ID
|
|
|
|
const targetObject =
|
|
findObjectById(this.canvas, this.objectId)?.object || null;
|
|
|
|
const { layer, parent } = findLayerRecursively(
|
|
this.layers.value,
|
|
targetObject?.layerId
|
|
);
|
|
|
|
this.layer = layer;
|
|
this.parent = parent;
|
|
|
|
this.isSginleObject = parent?.id === this.lastSelectLayerId?.value; // 是否需要记录遮罩的变换位置 如果是组图层且组下只有一个图层有对象 且 用户最后点击的是父图层 则记录遮罩变更
|
|
}
|
|
|
|
/**
|
|
* 执行命令
|
|
* 如果是首次执行,记录初始和最终状态
|
|
* 如果是重做,应用最终状态
|
|
*/
|
|
async execute() {
|
|
if (!this.finalState) {
|
|
console.warn("没有最终状态可应用");
|
|
return false;
|
|
}
|
|
|
|
// 查找目标对象
|
|
const targetObject = this._findObject(this.objectId);
|
|
if (!targetObject) {
|
|
console.warn(`未找到ID为 ${this.objectId} 的对象`);
|
|
return false;
|
|
}
|
|
|
|
// 应用最终变换状态
|
|
await this._applyTransform(targetObject, this.finalState);
|
|
|
|
setTimeout(() => {
|
|
// 现在图层就是元素本身,直接更新元素的缩略图
|
|
this?.layerManager?.canvasManager?.thumbnailManager?.generateLayerThumbnail?.(
|
|
this.layer.id
|
|
);
|
|
if (this.parent) {
|
|
// 如果有父图层,更新父图层的缩略图
|
|
this?.layerManager?.canvasManager?.thumbnailManager?.generateLayerThumbnail?.(
|
|
this.parent.id
|
|
);
|
|
}
|
|
}, 300);
|
|
|
|
// 触发画布更新
|
|
this.canvas.renderAll();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 撤销命令
|
|
* 应用初始状态
|
|
*/
|
|
async undo() {
|
|
if (!this.initialState) {
|
|
console.warn("没有初始状态可恢复");
|
|
return false;
|
|
}
|
|
|
|
// 查找目标对象
|
|
const targetObject = this._findObject(this.objectId);
|
|
if (!targetObject) {
|
|
console.warn(`未找到ID为 ${this.objectId} 的对象`);
|
|
return false;
|
|
}
|
|
|
|
// 应用初始变换状态
|
|
await this._applyTransform(targetObject, this.initialState);
|
|
|
|
setTimeout(() => {
|
|
// 现在图层就是元素本身,直接更新元素的缩略图
|
|
this?.layerManager?.canvasManager?.thumbnailManager?.generateLayerThumbnail?.(
|
|
this.layer.id
|
|
);
|
|
if (this.parent) {
|
|
// 如果有父图层,更新父图层的缩略图
|
|
this?.layerManager?.canvasManager?.thumbnailManager?.generateLayerThumbnail?.(
|
|
this.parent.id
|
|
);
|
|
}
|
|
}, 300);
|
|
// 触发画布更新
|
|
this.canvas.renderAll();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 查找对象
|
|
* @private
|
|
*/
|
|
_findObject(objectId) {
|
|
if (!this.canvas) return null;
|
|
return this.canvas.getObjects().find((obj) => obj.id === objectId);
|
|
}
|
|
|
|
/**
|
|
* 应用变换状态到对象
|
|
* @private
|
|
*/
|
|
async _applyTransform(object, transformState) {
|
|
if (!object || !transformState) return;
|
|
// 变换遮罩层 - 如果当前图层是组图层,且有遮罩并且组下只有一个图层有对象 则应用遮罩转换
|
|
if (
|
|
this.parent &&
|
|
this.parent?.clippingMask &&
|
|
// this.parent?.children?.length === 1 &&
|
|
this.isSginleObject
|
|
) {
|
|
// 计算对象的变换位置
|
|
const moveLeft = object.left - transformState.left; // 计算移动的水平距离
|
|
const moveTop = object.top - transformState.top; // 计算移动的垂直距离
|
|
|
|
this.parent.clippingMask.left -= moveLeft;
|
|
this.parent.clippingMask.top -= moveTop;
|
|
if (this.parent.selectObject) {
|
|
// 如果有选区 则选区位置也要更新
|
|
this.parent.selectObject.left = this.parent.clippingMask.left;
|
|
this.parent.selectObject.top = this.parent.clippingMask.top;
|
|
const { object } = findObjectById(
|
|
this.canvas,
|
|
this.parent.selectObject?.id
|
|
);
|
|
object?.set({
|
|
left: this.parent.clippingMask.left,
|
|
top: this.parent.clippingMask.top,
|
|
});
|
|
object?.setCoords();
|
|
}
|
|
|
|
// 重新创建遮罩对象
|
|
const clippingMaskFabricObject = await restoreFabricObject(
|
|
this.parent.clippingMask,
|
|
this.canvas
|
|
);
|
|
|
|
if (clippingMaskFabricObject) {
|
|
clippingMaskFabricObject.clipPath = null;
|
|
clippingMaskFabricObject.set({
|
|
absolutePositioned: true,
|
|
});
|
|
|
|
clippingMaskFabricObject.dirty = true;
|
|
clippingMaskFabricObject.setCoords();
|
|
|
|
// const clippingMask = this.parent.clippingMask;
|
|
// object.clipPath = clippingMask;
|
|
|
|
object.clipPath = clippingMaskFabricObject;
|
|
object.dirty = true;
|
|
}
|
|
}
|
|
|
|
// 应用变换属性,只设置真正变化的值
|
|
Object.entries(transformState).forEach(([key, value]) => {
|
|
object.set(key, value);
|
|
});
|
|
|
|
// 确保对象更新
|
|
object.setCoords();
|
|
}
|
|
|
|
/**
|
|
* 获取命令信息
|
|
*/
|
|
getInfo() {
|
|
return {
|
|
name: this.name,
|
|
description: this.description,
|
|
objectId: this.objectId,
|
|
objectType: this.objectType,
|
|
changedProps: this.finalState ? Object.keys(this.finalState) : [],
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 捕获对象的变换状态
|
|
* @static
|
|
*/
|
|
static captureTransformState(object) {
|
|
if (!object) return null;
|
|
|
|
// 只捕获变换相关的属性
|
|
return {
|
|
left: object.left,
|
|
top: object.top,
|
|
scaleX: object.scaleX,
|
|
scaleY: object.scaleY,
|
|
angle: object.angle,
|
|
flipX: object.flipX,
|
|
flipY: object.flipY,
|
|
skewX: object.skewX,
|
|
skewY: object.skewY,
|
|
};
|
|
}
|
|
}
|