Files
aida_front/src/component/Canvas/CanvasEditor/commands/ClearSelectionContentCommand.js
2025-07-14 01:00:23 +08:00

494 lines
15 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 { fabric } from "fabric-with-all";
import { Command } from "./Command";
import { createRasterizedImage } from "../utils/selectionToImage";
import { deepClone, findObjectById, generateId } from "../utils/helper";
import { findLayerRecursively } from "../utils/layerHelper";
/**
* 从选区中删除内容命令
* 使用栅格化方式清除选区内容,支持复杂选区形状
*/
export class ClearSelectionContentCommand extends Command {
constructor(options = {}) {
super({
name: "清除选区内容",
description: "使用栅格化方式删除选区中的内容",
saveState: true,
});
this.canvas = options.canvas;
this.layers = options.layerManager?.layers;
this.layerManager = options.layerManager;
this.selectionManager = options.selectionManager;
this.targetLayerId = options.layerManager.getActiveLayerId(); // 默认使用当前活动图层
this.removedObjects = [];
this.oldLayer = [...this.layers.value]; // 获取原图层对象
const { layer } = findLayerRecursively(this.layers.value, this.targetLayerId) || {};
// 栅格化相关属性
this.originalLayerBackup = null;
const { object: originalLayerObject } = findObjectById(
this.canvas,
layer.fabricObjects?.[0]?.id
);
// this.originalLayerObject = fabric.util.object.clone(originalLayerObject); // 获取原图层对象
this.oldLayerObjects = originalLayerObject.toObject(["id", "layerId", "layerName"]); // 获取原图层的所有对象
this.rasterizedImage = null;
this.targetLayer = findLayerRecursively(this.canvas, this.targetLayerId)?.layer;
this.layerRasterized = false;
// 高清设置
this.highResolutionEnabled = options.highResolutionEnabled !== false;
this.baseResolutionScale = options.baseResolutionScale || 2;
// 支持外部传入选区对象,优先使用外部传入的选区对象
if (options.selectionObject) {
// 外部传入的选区对象,直接使用
this.selectionObject = options.selectionObject;
console.log("清除选区内容:使用外部传入的选区对象");
} else {
// 从选区管理器获取选区对象
const currentSelection = this.selectionManager?.getSelectionObject();
if (currentSelection) {
this.selectionObject = fabric.util.object.clone(currentSelection);
console.log("清除选区内容:从选区管理器获取选区对象");
} else {
this.selectionObject = null;
console.warn("清除选区内容:没有可用的选区对象");
}
}
}
async execute() {
if (!this.canvas || !this.layerManager) {
console.error("无法清除选区内容:参数无效");
return false;
}
try {
// 获取选区
if (!this.selectionObject) {
console.error("无法清除选区内容:当前没有选区");
return false;
}
// 确定目标图层
const layerId = this.targetLayerId || this.layerManager.getActiveLayerId();
this.targetLayer = this.layerManager.getLayerById(layerId);
if (!this.targetLayer) {
console.error("无法清除选区内容:目标图层无效");
return false;
}
// 备份原图层状态
// this.originalLayerBackup = JSON.parse(JSON.stringify(this.targetLayer));
// 获取图层的所有对象
const layerObjects = this._getLayerObjects(this.targetLayer);
if (layerObjects.length === 0) {
console.warn("目标图层没有对象,无需清除");
return true;
}
// 创建反转选区(保留选区外的内容)
const invertedSelection = await this._createInvertedSelection(this.selectionObject);
if (!invertedSelection) {
console.error("创建反转选区失败");
return false;
}
// 使用栅格化方式清除选区内容
await this._rasterizeLayerWithClearance(layerObjects, invertedSelection);
// 清除选区
this.selectionManager?.clearSelection?.();
console.log("✅ 选区内容清除完成");
return { cleared: true, layerId: this.targetLayer.id };
} catch (error) {
console.error("清除选区内容失败:", error);
// 尝试恢复原状态
await this._restoreOriginalState();
throw error;
}
}
async undo() {
try {
this.layers.value = this.oldLayer; // 恢复原图层对象
// 恢复原图层状态
// 移除栅格化后的图像
if (this.rasterizedImage && this.canvas.getObjects().includes(this.rasterizedImage)) {
this.canvas.remove(this.rasterizedImage);
}
// 恢复图层的fabricObjects
// 重新创建并添加对象到画布
if (this.targetLayer.fabricObjects.length > 0) {
await new Promise((resolve) => {
fabric.util.enlivenObjects([this.oldLayerObjects], (objects) => {
objects.forEach((obj) => {
// 确保对象有正确的ID和图层信息
obj.set({
layerId: this.targetLayer.id,
layerName: this.targetLayer.name,
selectable: true,
evented: true,
});
// 添加到画布
this.canvas.add(obj);
this.targetLayer.fabricObjects = [
obj.toObject(["id", "layerId", "layerName", "type", "custom"]),
];
});
this.canvas.renderAll();
resolve();
});
});
}
await this.layerManager?.updateLayersObjectsInteractivity?.(true);
this.layerRasterized = false;
this.rasterizedImage = null;
console.log("✅ 清除选区内容撤销完成");
return true;
} catch (error) {
console.error("❌ 撤销清除选区内容失败:", error);
return false;
}
}
/**
* 使用栅格化方式清除选区内容
* @param {Array} layerObjects 图层对象数组
* @param {Object} invertedSelection 反转选区
* @private
*/
async _rasterizeLayerWithClearance(layerObjects, invertedSelection) {
try {
console.log("🖼️ 开始栅格化图层并清除选区内容");
// 确定缩放因子
let scaleFactor = this.baseResolutionScale;
if (this.highResolutionEnabled) {
const currentZoom = this.canvas.getZoom?.() || 1;
scaleFactor = Math.max(scaleFactor || this.canvas?.getRetinaScaling?.(), currentZoom);
scaleFactor = Math.min(scaleFactor, 3);
}
// 使用createRasterizedImage生成栅格化图像使用反转选区作为裁剪路径
this.rasterizedImage = await createRasterizedImage({
canvas: this.canvas,
fabricObjects: layerObjects,
clipPath: invertedSelection, // 使用反转选区,保留选区外的内容
trimWhitespace: false, // 保持原始尺寸
trimPadding: 0,
quality: 1.0,
format: "png",
scaleFactor: scaleFactor,
preserveOriginalQuality: true,
selectionManager: this.selectionManager,
});
if (!this.rasterizedImage) {
throw new Error("栅格化图层失败");
}
// 移除原图层的所有对象
for (const obj of layerObjects) {
if (this.canvas.getObjects().includes(obj)) {
this.canvas.remove(obj);
}
}
// 设置栅格化图像的属性
this.rasterizedImage.set({
id: generateId("cleared_"),
layerId: this.targetLayer.id,
layerName: this.targetLayer.name,
selectable: true,
evented: true,
});
// 添加栅格化图像到画布和图层
this.canvas.add(this.rasterizedImage);
this.targetLayer.fabricObjects = [
this.rasterizedImage.toObject("id", "layerId", "layerName", "parentId"),
];
this.layerRasterized = true;
this.canvas.renderAll();
console.log("✅ 图层栅格化清除完成");
} catch (error) {
console.error("栅格化图层清除失败:", error);
throw error;
}
}
/**
* 创建反转选区
* @param {Object} selectionObject 原选区对象
* @returns {Object} 反转选区对象
* @private
*/
async _createInvertedSelection(selectionObject) {
try {
// 获取目标图层的所有对象
const layerObjects = this._getLayerObjects(this.targetLayer);
if (layerObjects.length === 0) {
console.warn("目标图层没有对象,无法创建反选区");
return null;
}
// 计算图层对象的边界框
const layerBounds = this._calculateLayerBounds(layerObjects);
if (!layerBounds) {
console.warn("无法计算图层边界框");
return null;
}
console.log("📐 图层边界框:", layerBounds);
console.log("🎯 选区路径:", selectionObject.path);
// 创建反转选区路径,使用图层边界而不是画布边界
const pathString = `M ${layerBounds.left} ${layerBounds.top} L ${
layerBounds.left + layerBounds.width
} ${layerBounds.top} L ${layerBounds.left + layerBounds.width} ${
layerBounds.top + layerBounds.height
} L ${layerBounds.left} ${layerBounds.top + layerBounds.height} Z ${selectionObject.path}`;
const invertedPath = new fabric.Path(pathString, {
fillRule: "evenodd",
selectable: false,
evented: false,
fill: "#ffffff", // 确保填充颜色
stroke: "",
strokeWidth: 0,
id: `inverted_selection_${Date.now()}`,
name: "inverted_selection",
});
console.log("✅ 反转选区创建完成,使用图层边界框");
return invertedPath;
} catch (error) {
console.error("创建反转选区失败:", error);
return null;
}
}
/**
* 计算图层对象的边界框
* @param {Array} layerObjects 图层对象数组
* @returns {Object|null} 边界框 {left, top, width, height}
* @private
*/
_calculateLayerBounds(layerObjects) {
if (!layerObjects || layerObjects.length === 0) {
return null;
}
let bounds = null;
layerObjects.forEach((obj) => {
// 获取对象的边界框(包含变换)
const objBounds = obj.getBoundingRect(true, true);
if (!bounds) {
bounds = { ...objBounds };
} else {
const right = Math.max(bounds.left + bounds.width, objBounds.left + objBounds.width);
const bottom = Math.max(bounds.top + bounds.height, objBounds.top + objBounds.height);
bounds.left = Math.min(bounds.left, objBounds.left);
bounds.top = Math.min(bounds.top, objBounds.top);
bounds.width = right - bounds.left;
bounds.height = bottom - bounds.top;
}
});
// 添加一些边距以确保完整覆盖
if (bounds) {
const padding = 2;
bounds.left -= padding;
bounds.top -= padding;
bounds.width += padding * 2;
bounds.height += padding * 2;
}
return bounds;
}
/**
* 获取图层的所有对象
* @param {Object} layer 图层对象
* @returns {Array} fabric对象数组
* @private
*/
_getLayerObjects(layer) {
const objects = [];
const canvasObjects = this.canvas.getObjects();
// 递归获取图层及其子图层的所有对象
const collectLayerObjects = (currentLayer) => {
// 处理图层的fabricObjects
if (currentLayer.fabricObjects && Array.isArray(currentLayer.fabricObjects)) {
currentLayer.fabricObjects.forEach((fabricObj) => {
if (fabricObj && fabricObj.id) {
const realObject = canvasObjects.find((canvasObj) => canvasObj.id === fabricObj.id);
if (realObject && realObject.visible !== false) {
objects.push(realObject);
}
}
});
}
// 处理单个fabricObject背景图层等
if (currentLayer.fabricObject && currentLayer.fabricObject.id) {
const realObject = canvasObjects.find(
(canvasObj) => canvasObj.id === currentLayer.fabricObject.id
);
if (realObject && realObject.visible !== false) {
objects.push(realObject);
}
}
// 递归处理子图层
if (currentLayer.children && Array.isArray(currentLayer.children)) {
currentLayer.children.forEach((childLayer) => {
collectLayerObjects(childLayer);
});
}
};
collectLayerObjects(layer);
return objects;
}
/**
* 恢复原图层状态
* @private
*/
async _restoreOriginalState() {
try {
if (!this.originalLayerBackup || !this.targetLayer) {
console.warn("没有原图层备份数据或目标图层");
return;
}
// 移除栅格化后的图像
if (this.rasterizedImage && this.canvas.getObjects().includes(this.rasterizedImage)) {
this.canvas.remove(this.rasterizedImage);
}
// 恢复图层的fabricObjects
this.targetLayer.fabricObjects = this.originalLayerBackup.fabricObjects || [];
// 重新创建并添加对象到画布
if (this.targetLayer.fabricObjects.length > 0) {
await this._restoreLayerObjects(this.targetLayer);
}
this.layerRasterized = false;
this.rasterizedImage = null;
console.log("✅ 原图层状态恢复完成");
} catch (error) {
console.error("恢复原图层状态失败:", error);
}
}
/**
* 恢复图层对象到画布
* @param {Object} layer 图层对象
* @private
*/
async _restoreLayerObjects(layer) {
return new Promise((resolve) => {
if (!layer.fabricObjects || layer.fabricObjects.length === 0) {
resolve();
return;
}
fabric.util.enlivenObjects(layer.fabricObjects, (objects) => {
objects.forEach((obj) => {
// 确保对象有正确的ID和图层信息
obj.set({
layerId: layer.id,
layerName: layer.name,
selectable: true,
evented: true,
});
// 添加到画布
this.canvas.add(obj);
});
this.canvas.renderAll();
resolve();
});
});
}
_cloneObjectSync(obj) {
// 这是一个简单的深拷贝,不适用于所有场景
// 在实际应用中应该使用fabric.js的clone方法
if (!obj) return null;
return JSON.parse(JSON.stringify(obj));
}
async _cloneObject(obj) {
return new Promise((resolve, reject) => {
if (!obj) {
reject(new Error("对象无效,无法克隆"));
return;
}
try {
if (typeof obj.clone === "function") {
obj.clone((cloned) => {
resolve(cloned);
});
} else {
// 如果对象没有clone方法(可能是因为它已经是序列化后的对象)
// 在实际代码中需要适当处理这种情况
resolve(Object.assign({}, obj));
}
} catch (error) {
reject(error);
}
});
}
/**
* 获取命令信息
* @returns {Object} 命令详细信息
*/
getInfo() {
return {
name: this.name,
description: this.description,
targetLayerId: this.targetLayer?.id,
targetLayerName: this.targetLayer?.name,
layerRasterized: this.layerRasterized,
hasOriginalBackup: !!this.originalLayerBackup,
removedObjectsCount: this.removedObjects.length,
highResolutionEnabled: this.highResolutionEnabled,
baseResolutionScale: this.baseResolutionScale,
};
}
}