feat: 优化部分问题,完善选区功能

This commit is contained in:
bighuixiang
2025-07-04 03:16:18 +08:00
parent b6afd2764d
commit 0615ab31f9
12 changed files with 1892 additions and 1144 deletions

View File

@@ -0,0 +1,527 @@
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,
};
}
}