feat: 实时更新背景色+选取剪切+选取删除初步开发完成

This commit is contained in:
bighuixiang
2025-07-03 00:04:05 +08:00
parent eb1848bd6d
commit b6afd2764d
8 changed files with 1130 additions and 106 deletions

View File

@@ -130,13 +130,15 @@ export class UpdateBackgroundCommand extends Command {
});
this.canvas = options.canvas;
this.layers = options.layers;
this.canvasManager = options.canvasManager;
this.backgroundColorValue = options.backgroundColorValue; // 使用.value获取实际值
this.backgroundColor = options.backgroundColor; //
this.historyManager = options.historyManager;
this.oldColor = options.oldColor; // 旧颜色,用于撤销
// 查找背景图层
this.bgLayer = this.layers.value.find((layer) => layer.isBackground);
this.oldBackgroundColor = this.bgLayer.backgroundColor;
this.oldBackgroundColor = this.oldColor;
this.backgroundObject = findObjectById(
this.canvas,
this.bgLayer.fabricObject.id
@@ -159,6 +161,11 @@ export class UpdateBackgroundCommand extends Command {
this.canvas.renderAll();
}
this.backgroundColorValue.value = this.backgroundColor; // 设置背景颜色
// 生成缩略图
this.canvasManager?.thumbnailManager?.generateLayerThumbnail?.(
this.bgLayer.id
);
return true;
}
@@ -177,6 +184,12 @@ export class UpdateBackgroundCommand extends Command {
this.canvas.renderAll();
}
this.backgroundColorValue.value = this.oldBackgroundColor; // 恢复背景颜色
// 生成缩略图
this.canvasManager?.thumbnailManager?.generateLayerThumbnail?.(
this.bgLayer.id
);
// 如果有旧颜色,恢复到旧颜色
return true;
}

View File

@@ -4,10 +4,10 @@ import {
LayerType,
} from "../utils/layerHelper.js";
import { createRasterizedImage } from "../utils/selectionToImage.js";
import { CompositeCommand } from "./Command.js";
import { CompositeCommand, Command } from "./Command.js";
import { CreateImageLayerCommand } from "./LayerCommands.js";
import { ClearSelectionCommand } from "./SelectionCommands.js";
import { fabric } from "fabric-with-all";
import { generateId } from "../utils/helper.js";
/**
* 套索抠图命令
@@ -167,10 +167,10 @@ export class LassoCutoutCommand extends CompositeCommand {
// 插入新组图层
this.layerManager.layers.value.splice(topLayerIndex, 1, this.groupLayer);
await this.layerManager.updateLayersObjectsInteractivity();
this.canvas.discardActiveObject();
this.canvas.setActiveObject(this.fabricImage);
this.canvas.renderAll();
await this.layerManager.updateLayersObjectsInteractivity(true);
console.log(`套索抠图完成新图层ID: ${this.newLayerId}`);
return {
newLayerId: this.newLayerId,
@@ -275,7 +275,7 @@ export class LassoCutoutCommand extends CompositeCommand {
// 注意不重置groupId因为重做时可能需要使用相同的ID
// 4. 更新画布和图层交互性
await this.layerManager.updateLayersObjectsInteractivity();
await this.layerManager.updateLayersObjectsInteractivity(true);
console.log(`✅ 套索抠图撤销完成`);
return true;
@@ -783,3 +783,763 @@ export class LassoCutoutCommand extends CompositeCommand {
}
}
}
/**
* 剪切选区到新图层命令
* 实现将选区内容剪切到新图层,同时对原图层进行栅格化抠图的功能
*/
export class CutSelectionToNewLayerCommand extends CompositeCommand {
constructor(options = {}) {
super([], {
name: "剪切选区到新图层",
description: "将选区剪切到新图层并对原图层进行栅格化",
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.selectionManager = options.selectionManager;
this.toolManager = options.toolManager;
this.sourceLayerId = options.sourceLayerId;
this.newLayerName = options.newLayerName || "剪切";
this.newLayerId = null;
this.cutoutImageUrl = null;
this.fabricImage = null;
this.executedCommands = [];
// 高清截图选项
this.highResolutionEnabled = options.highResolutionEnabled !== false; // 默认启用
this.baseResolutionScale = options.baseResolutionScale || 2; // 基础分辨率倍数
this.groupId = options.groupId || `cut-group-${Date.now()}`;
this.groupName = options.groupName || `剪切组`;
this.groupLayer = null; // 保存组图层的引用
this.originalLayersLength = 0; // 保存原始图层数量
// 原图层栅格化相关
this.originalLayerRasterized = false;
this.originalLayerBackup = null;
this.rasterizedOriginalImage = null;
// 序列化保存选区对象,用于重做时恢复
this.serializedSelectionObject = null;
this._serializeSelectionObject();
}
async execute() {
if (!this.canvas || !this.layerManager || !this.selectionManager) {
console.error("无法执行剪切选区:参数无效");
return false;
}
try {
this.executedCommands = [];
// 保存原始图层数量,用于撤销时的验证
this.originalLayersLength = this.layerManager.layers.value.length;
// 获取选区
const selectionObject = await this._getSelectionObject();
if (!selectionObject) {
console.error("无法执行剪切选区:当前没有选区");
return false;
}
// 确定源图层
const sourceLayer = this.layerManager.getActiveLayer();
if (!sourceLayer) {
console.error("无法执行剪切选区:没有活动图层");
return false;
}
// 备份原图层状态
this.originalLayerBackup = JSON.parse(JSON.stringify(sourceLayer));
// 获取源图层的所有对象(包括子图层)
const sourceObjects = this._getLayerObjects(sourceLayer);
if (sourceObjects.length === 0) {
console.error("无法执行剪切选区:源图层没有可见对象");
return false;
}
// 获取选区边界信息用于后续定位
const selectionBounds = selectionObject.getBoundingRect(true, true);
// 步骤1: 先创建抠图到新图层(复制选区内容)
this.fabricImage = await this._performCutoutWithRasterized(
sourceObjects,
selectionObject,
selectionBounds
);
if (!this.fabricImage) {
console.error("抠图失败");
return false;
}
// 步骤2: 创建图像图层命令
const createImageLayerCmd = new CreateImageLayerCommand({
layerManager: this.layerManager,
fabricImage: this.fabricImage,
toolManager: this.toolManager,
layerName: this.newLayerName,
});
// 执行创建图像图层命令
const result = await createImageLayerCmd.execute();
this.newLayerId = createImageLayerCmd.newLayerId;
this.executedCommands.push(createImageLayerCmd);
// 步骤3: 对原图层进行栅格化,移除选区内容
await this._rasterizeOriginalLayerWithCutout(
sourceLayer,
selectionObject,
selectionBounds
);
// 步骤4: 清除选区命令
const clearSelectionCmd = new ClearSelectionCommand({
canvas: this.canvas,
selectionManager: this.selectionManager,
});
// 执行清除选区命令
await clearSelectionCmd.execute();
this.executedCommands.push(clearSelectionCmd);
const topLayerIndex = this.layerManager.layers.value.findIndex(
(layer) => layer.id === this.newLayerId
);
const selectLayer = this.layerManager.layers.value[topLayerIndex];
// 创建新的组图层
this.groupLayer = createLayer({
id: this.groupId,
name: this.groupName || `剪切组`,
type: LayerType.GROUP,
visible: true,
locked: false,
opacity: 1.0,
fabricObjects: [],
children: [],
});
this.fabricImage.set({
selectable: true,
evented: true,
});
selectLayer.parentId = this.groupId;
selectLayer.fabricObjects = [
this.fabricImage.toObject("id", "layerId", "layerName", "parentId"),
];
this.groupLayer.clippingMask = this.fabricImage.toObject(
"id",
"layerId",
"layerName",
"parentId"
);
this.groupLayer.children.push(selectLayer);
// 插入新组图层
this.layerManager.layers.value.splice(topLayerIndex, 1, this.groupLayer);
this.canvas.discardActiveObject();
this.canvas.setActiveObject(this.fabricImage);
await this.layerManager.updateLayersObjectsInteractivity(true);
console.log(`剪切选区完成新图层ID: ${this.newLayerId}`);
return {
newLayerId: this.newLayerId,
cutoutImageUrl: this.cutoutImageUrl,
groupId: this.groupId,
groupName: this.groupName,
};
} catch (error) {
console.error("剪切选区过程中出错:", error);
// 错误清理和回滚
await this._handleExecutionError();
throw error;
}
}
async undo() {
try {
console.log(`↩️ 开始撤销剪切选区操作`);
// 1. 首先移除组图层(如果存在)
if (this.groupId) {
const groupIndex = this.layerManager.layers.value.findIndex(
(layer) => layer.id === this.groupId
);
if (groupIndex !== -1) {
this.layerManager.layers.value.splice(groupIndex, 1);
console.log(`移除了组图层: ${this.groupId}`);
}
}
if (this.fabricImage) {
// 从画布移除抠图对象
if (this.canvas.getObjects().includes(this.fabricImage)) {
this.canvas.remove(this.fabricImage);
}
}
// 2. 恢复原图层的状态(如果进行了栅格化)
if (this.originalLayerRasterized && this.originalLayerBackup) {
await this._restoreOriginalLayer();
}
// 3. 逆序撤销所有已执行的子命令
for (let i = this.executedCommands.length - 1; i >= 0; i--) {
const command = this.executedCommands[i];
try {
if (command && typeof command.undo === "function") {
await command.undo();
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
}
} catch (error) {
console.error(
`❌ 子命令撤销失败: ${command.constructor.name}`,
error
);
// 子命令撤销失败不中断整个撤销过程
}
}
// 4. 清理状态
this.executedCommands = [];
this.newLayerId = null;
this.cutoutImageUrl = null;
this.fabricImage = null;
this.groupLayer = null;
this.originalLayerRasterized = false;
this.rasterizedOriginalImage = null;
// 5. 更新画布和图层交互性
await this.layerManager.updateLayersObjectsInteractivity(true);
console.log(`✅ 剪切选区撤销完成`);
return true;
} catch (error) {
console.error("❌ 撤销剪切选区失败:", error);
return false;
}
}
/**
* 对原图层进行栅格化,移除选区内容
* @param {Object} sourceLayer 源图层
* @param {Object} selectionObject 选区对象
* @param {Object} selectionBounds 选区边界
* @private
*/
async _rasterizeOriginalLayerWithCutout(
sourceLayer,
selectionObject,
selectionBounds
) {
try {
console.log("🔪 开始对原图层进行栅格化剪切");
// 获取源图层的所有对象
const sourceObjects = this._getLayerObjects(sourceLayer);
if (sourceObjects.length === 0) {
console.warn("源图层没有对象需要栅格化");
return;
}
// 创建反转选区(即选区外的内容)
const invertedSelection = await this._createInvertedSelection(
selectionObject
);
if (!invertedSelection) {
console.error("创建反转选区失败");
return;
}
// 使用反转选区对原图层进行栅格化(保留选区外的内容)
this.rasterizedOriginalImage = await createRasterizedImage({
canvas: this.canvas,
fabricObjects: sourceObjects,
clipPath: invertedSelection,
trimWhitespace: true,
trimPadding: 2,
quality: 1.0,
format: "png",
scaleFactor: this.baseResolutionScale,
preserveOriginalQuality: true,
selectionManager: this.selectionManager,
});
if (!this.rasterizedOriginalImage) {
console.error("原图层栅格化失败");
return;
}
// 移除原图层的所有对象
for (const obj of sourceObjects) {
if (this.canvas.getObjects().includes(obj)) {
this.canvas.remove(obj);
}
}
// 将栅格化后的图像设置到原图层
this.rasterizedOriginalImage.set({
id: generateId("rasterized_"),
layerId: sourceLayer.id,
layerName: sourceLayer.name,
selectable: true,
evented: true,
});
// 添加栅格化图像到画布和图层
this.canvas.add(this.rasterizedOriginalImage);
sourceLayer.fabricObjects = [
this.rasterizedOriginalImage.toObject(
"id",
"layerId",
"layerName",
"parentId"
),
];
this.originalLayerRasterized = true;
this.canvas.renderAll();
console.log("✅ 原图层栅格化剪切完成");
} catch (error) {
console.error("原图层栅格化剪切失败:", error);
throw error;
}
}
/**
* 创建反转选区
* @param {Object} selectionObject 原选区对象
* @returns {Object} 反转选区对象
* @private
*/
async _createInvertedSelection(selectionObject) {
try {
// 获取画布范围
const canvasRect = new fabric.Rect({
left: 0,
top: 0,
width: this.canvas.width,
height: this.canvas.height,
selectable: false,
});
// 创建反选路径
let invertedPath;
try {
// 使用 fabric.js 的布尔运算创建反转选区
const canvasPath = canvasRect.toClipPathSVG();
const selectionPath = selectionObject.toClipPathSVG();
// 创建反转选区对象
const pathString = `M 0 0 L ${this.canvas.width} 0 L ${this.canvas.width} ${this.canvas.height} L 0 ${this.canvas.height} Z ${selectionObject.path}`;
invertedPath = new fabric.Path(pathString, {
fillRule: "evenodd",
selectable: false,
evented: false,
id: `inverted_selection_${Date.now()}`,
name: "inverted_selection",
});
} catch (error) {
console.error("无法创建反转选区:", error);
return null;
}
return invertedPath;
} catch (error) {
console.error("创建反转选区失败:", error);
return null;
}
}
/**
* 恢复原图层状态
* @private
*/
async _restoreOriginalLayer() {
try {
if (!this.originalLayerBackup) {
console.warn("没有原图层备份数据");
return;
}
// 移除栅格化后的图像
if (
this.rasterizedOriginalImage &&
this.canvas.getObjects().includes(this.rasterizedOriginalImage)
) {
this.canvas.remove(this.rasterizedOriginalImage);
}
// 恢复原图层的对象
const sourceLayer = this.layerManager.getLayerById(
this.originalLayerBackup.id
);
if (sourceLayer) {
// 恢复图层的fabricObjects
sourceLayer.fabricObjects =
this.originalLayerBackup.fabricObjects || [];
// 重新创建并添加对象到画布
if (sourceLayer.fabricObjects.length > 0) {
await this._restoreLayerObjects(sourceLayer);
}
}
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();
});
});
}
/**
* 处理执行错误时的清理
* @private
*/
async _handleExecutionError() {
try {
// 如果已经创建了新图层,需要进行清理
if (this.newLayerId) {
try {
const layerIndex = this.layerManager.layers.value.findIndex(
(layer) => layer.id === this.newLayerId
);
if (layerIndex !== -1) {
this.layerManager.layers.value.splice(layerIndex, 1);
}
console.log(`清理了异常创建的新图层: ${this.newLayerId}`);
} catch (cleanupError) {
console.warn("清理新图层失败:", cleanupError);
}
}
// 清理组图层(如果已创建)
if (this.groupLayer && this.groupId) {
try {
const groupIndex = this.layerManager.layers.value.findIndex(
(layer) => layer.id === this.groupId
);
if (groupIndex !== -1) {
this.layerManager.layers.value.splice(groupIndex, 1);
console.log(`清理了异常创建的组图层: ${this.groupId}`);
}
this.groupLayer = null;
} catch (cleanupError) {
console.warn("清理组图层失败:", cleanupError);
}
}
// 恢复原图层状态(如果已经栅格化)
if (this.originalLayerRasterized) {
await this._restoreOriginalLayer();
}
// 尝试回滚已执行的命令
if (this.executedCommands.length > 0) {
for (let i = this.executedCommands.length - 1; i >= 0; i--) {
const command = this.executedCommands[i];
try {
if (command && typeof command.undo === "function") {
await command.undo();
}
} catch (rollbackError) {
console.warn(
`回滚命令失败: ${command.constructor.name}`,
rollbackError
);
}
}
}
} catch (error) {
console.error("错误清理过程中出现问题:", error);
}
}
/**
* 获取命令信息
* @returns {Object} 命令详细信息
*/
getInfo() {
return {
name: this.name,
description: this.description,
newLayerId: this.newLayerId,
newLayerName: this.newLayerName,
groupId: this.groupId,
groupName: this.groupName,
executedCommandsCount: this.executedCommands.length,
hasGroupLayer: !!this.groupLayer,
sourceLayerId: this.sourceLayerId,
highResolutionEnabled: this.highResolutionEnabled,
baseResolutionScale: this.baseResolutionScale,
hasSerializedSelection: !!this.serializedSelectionObject,
selectionType: this.serializedSelectionObject?.type || null,
originalLayerRasterized: this.originalLayerRasterized,
hasOriginalLayerBackup: !!this.originalLayerBackup,
subCommands: this.executedCommands.map((cmd) => ({
name: cmd.constructor.name,
info: cmd.getInfo ? cmd.getInfo() : {},
})),
};
}
/**
* 使用createRasterizedImage执行抠图操作
* @param {Array} sourceObjects 源对象数组
* @param {Object} selectionObject 选区对象
* @param {Object} selectionBounds 选区边界
* @returns {fabric.Image} 抠图结果的fabric图像对象
* @private
*/
async _performCutoutWithRasterized(
sourceObjects,
selectionObject,
selectionBounds
) {
try {
console.log("=== 开始使用createRasterizedImage执行剪切抠图 ===");
console.log(`源对象数量: ${sourceObjects.length}`);
console.log(`选区边界:`, selectionBounds);
// 确定缩放因子,确保高质量输出
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生成栅格化图像将选区作为裁剪路径
const rasterizedImage = await createRasterizedImage({
canvas: this.canvas,
fabricObjects: sourceObjects,
clipPath: selectionObject,
trimWhitespace: true,
trimPadding: 2,
quality: 1.0,
format: "png",
scaleFactor: scaleFactor,
preserveOriginalQuality: true,
selectionManager: this.selectionManager,
});
if (!rasterizedImage) {
console.error("高质量剪切抠图失败");
return null;
}
console.log(`✅ 高质量剪切抠图完成,缩放因子: ${scaleFactor}x`);
return rasterizedImage;
} catch (error) {
console.error("使用createRasterizedImage执行剪切抠图失败:", error);
throw error;
}
}
/**
* 获取图层的所有对象(包括子图层,从画布中查找真实对象)
* @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);
console.log(`从图层 "${layer.name}" 收集到 ${objects.length} 个可见对象`);
return objects;
}
/**
* 序列化选区对象
* @private
*/
_serializeSelectionObject() {
try {
if (!this.selectionManager) {
console.warn("选区管理器不存在,无法序列化选区对象");
return;
}
const selectionObject = this.selectionManager.getSelectionObject();
if (!selectionObject) {
console.warn("当前没有选区对象,无法序列化");
return;
}
// 将选区对象转换为可序列化的对象
this.serializedSelectionObject = selectionObject.toObject([
"id",
"layerId",
"layerName",
"parentId",
]);
console.log("选区对象已序列化保存");
} catch (error) {
console.error("序列化选区对象失败:", error);
this.serializedSelectionObject = null;
}
}
/**
* 反序列化选区对象
* @returns {Promise<Object>} 选区对象
* @private
*/
async _getSelectionObject() {
try {
// 首先尝试从选区管理器获取当前选区
if (this.selectionManager) {
const currentSelection = this.selectionManager.getSelectionObject();
if (currentSelection) {
return currentSelection;
}
}
// 如果没有当前选区,尝试从序列化数据恢复
if (this.serializedSelectionObject) {
return new Promise((resolve, reject) => {
fabric.util.enlivenObjects(
[this.serializedSelectionObject],
(objects) => {
if (objects && objects.length > 0) {
resolve(objects[0]);
} else {
reject(new Error("无法从序列化数据恢复选区对象"));
}
}
);
});
}
return null;
} catch (error) {
console.error("获取选区对象失败:", error);
return null;
}
}
}
/**
* 清除选区命令
*/
export class ClearSelectionCommand extends Command {
constructor(options = {}) {
super({
name: "清除选区",
description: "清除当前选区",
saveState: false,
});
this.selectionManager = options.selectionManager;
this.originalSelection = options.selectionManager
? options.selectionManager.getSelectionPath()
: null;
}
async execute() {
if (!this.selectionManager) {
console.error("无法清除选区:参数无效");
return false;
}
// 保存原始选区
if (!this.originalSelection) {
this.originalSelection = this.selectionManager.getSelectionPath();
}
// 清除选区
this.selectionManager.clearSelection();
return true;
}
async undo() {
if (!this.selectionManager || !this.originalSelection) return false;
// 恢复原始选区
this.selectionManager.setSelectionFromPath(this.originalSelection);
return true;
}
}

View File

@@ -1,6 +1,8 @@
import { Command, CompositeCommand } from "./Command.js";
import { fabric } from "fabric-with-all";
import { createLayer, LayerType } from "../utils/layerHelper.js";
import { createRasterizedImage } from "../utils/selectionToImage.js";
import { generateId } from "../utils/helper.js";
/**
* 创建选区命令
@@ -168,47 +170,6 @@ export class RemoveFromSelectionCommand extends Command {
}
}
/**
* 清除选区命令
*/
export class ClearSelectionCommand extends Command {
constructor(options = {}) {
super({
name: "清除选区",
description: "清除当前选区",
saveState: false,
});
this.selectionManager = options.selectionManager;
this.originalSelection = options.selectionManager
? options.selectionManager.getSelectionPath()
: null;
}
async execute() {
if (!this.selectionManager) {
console.error("无法清除选区:参数无效");
return false;
}
// 保存原始选区
if (!this.originalSelection) {
this.originalSelection = this.selectionManager.getSelectionPath();
}
// 清除选区
this.selectionManager.clearSelection();
return true;
}
async undo() {
if (!this.selectionManager || !this.originalSelection) return false;
// 恢复原始选区
this.selectionManager.setSelectionFromPath(this.originalSelection);
return true;
}
}
/**
* 羽化选区命令
*/
@@ -470,19 +431,30 @@ export class CopySelectionToNewLayerCommand extends CompositeCommand {
/**
* 从选区中删除内容命令
* 使用栅格化方式清除选区内容,支持复杂选区形状
*/
export class ClearSelectionContentCommand extends Command {
constructor(options = {}) {
super({
name: "清除选区内容",
description: "删除选区中的内容",
saveState: false,
description: "使用栅格化方式删除选区中的内容",
saveState: true,
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.selectionManager = options.selectionManager;
this.targetLayerId = options.targetLayerId;
this.removedObjects = [];
// 栅格化相关属性
this.originalLayerBackup = null;
this.rasterizedImage = null;
this.targetLayer = null;
this.layerRasterized = false;
// 高清设置
this.highResolutionEnabled = options.highResolutionEnabled !== false;
this.baseResolutionScale = options.baseResolutionScale || 2;
}
async execute() {
@@ -491,56 +463,297 @@ export class ClearSelectionContentCommand extends Command {
return false;
}
// 获取选区
const selectionObject = this.selectionManager.getSelectionObject();
if (!selectionObject) {
console.error("无法清除选区内容:当前没有选区");
return false;
try {
// 获取选区
const selectionObject = this.selectionManager.getSelectionObject();
if (!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(
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;
}
// 确定目标图层
const layerId = this.targetLayerId || this.layerManager.getActiveLayerId();
const layer = this.layerManager.getLayerById(layerId);
if (!layer || !layer.fabricObjects) {
console.error("无法清除选区内容:目标图层无效或为空");
return false;
}
// 找到选区内的对象
const objectsToRemove = layer.fabricObjects.filter((obj) => {
return this.selectionManager.isObjectInSelection(obj);
});
if (objectsToRemove.length === 0) {
console.warn("选区内没有对象需要清除");
return true;
}
// 备份被删除的对象
this.removedObjects = objectsToRemove.map((obj) => ({
object: this._cloneObjectSync(obj),
layerId: layerId,
}));
// 从图层中移除对象
for (const obj of objectsToRemove) {
this.layerManager.removeObjectFromLayer(layerId, obj.id);
}
return { removedCount: objectsToRemove.length };
}
async undo() {
if (!this.layerManager || this.removedObjects.length === 0) return false;
try {
console.log("↩️ 开始撤销清除选区内容操作");
// 恢复被删除的对象
for (const item of this.removedObjects) {
if (item.object && item.layerId) {
const clonedObj = await this._cloneObject(item.object);
this.layerManager.addObjectToLayer(item.layerId, clonedObj);
if (this.layerRasterized && this.originalLayerBackup) {
await this._restoreOriginalState();
} else if (this.removedObjects.length > 0) {
// 恢复被删除的对象(兼容旧版本)
for (const item of this.removedObjects) {
if (item.object && item.layerId) {
const clonedObj = await this._cloneObject(item.object);
this.layerManager.addObjectToLayer(clonedObj, item.layerId);
}
}
}
console.log("✅ 清除选区内容撤销完成");
return true;
} catch (error) {
console.error("❌ 撤销清除选区内容失败:", error);
return false;
}
return true;
}
/**
* 使用栅格化方式清除选区内容
* @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 pathString = `M 0 0 L ${this.canvas.width} 0 L ${this.canvas.width} ${this.canvas.height} L 0 ${this.canvas.height} Z ${selectionObject.path}`;
const invertedPath = new fabric.Path(pathString, {
fillRule: "evenodd",
selectable: false,
evented: false,
id: `inverted_selection_${Date.now()}`,
name: "inverted_selection",
});
return invertedPath;
} catch (error) {
console.error("创建反转选区失败:", error);
return null;
}
}
/**
* 获取图层的所有对象
* @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) {
@@ -572,7 +785,22 @@ export class ClearSelectionContentCommand extends Command {
}
});
}
}
// 导入套索抠图命令
export { LassoCutoutCommand } from "./LassoCutoutCommand.js";
/**
* 获取命令信息
* @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,
};
}
}

View File

@@ -43,6 +43,7 @@ const emit = defineEmits([
// 笔刷面板相关状态
const showBrushPanel = ref(false);
const brushPanelRef = ref(null);
const lastColor = ref("#ffffff");
// 计算属性
// const shouldShowBrushSettings = computed(() => {
@@ -82,17 +83,32 @@ function updateCanvasSize(
}
function updateCanvasColor() {
console.log("更新画布颜色:", props.canvasColor);
if (!layerManager) {
console.warn("LayerManager 未初始化,无法更改背景色");
return;
}
// 更新背景层颜色而不是画布颜色
layerManager.updateBackgroundColor(props.canvasColor);
layerManager.updateBackgroundColor(props.canvasColor, {
oldColor: lastColor.value,
undoable: true,
});
lastColor.value = props.canvasColor;
emit("canvas-color-change");
}
watch(
() => props.canvasColor,
(newColor) => {
// 更新背景层颜色而不是画布颜色
layerManager.updateBackgroundColor(newColor, {
oldColor: lastColor.value,
undoable: false, // 不需要撤销
});
}
);
// 切换笔刷面板显示状态
function toggleBrushPanel() {
// 如果笔刷没有激活 则激活笔刷工具
@@ -222,6 +238,7 @@ function showLayerPanel() {
}
onMounted(() => {
lastColor.value = props.canvasColor;
// 获取工具管理器和笔刷管理器
const brushManager = toolManager?.brushManager;

View File

@@ -186,14 +186,18 @@ import { useI18n } from "vue-i18n";
import {
CreateSelectionCommand,
InvertSelectionCommand,
ClearSelectionCommand,
FeatherSelectionCommand,
FillSelectionCommand,
CopySelectionToNewLayerCommand,
ClearSelectionContentCommand,
LassoCutoutCommand,
} from "../commands/SelectionCommands";
import { ToolCommand } from "../commands/ToolCommands";
import {
LassoCutoutCommand,
ClearSelectionCommand,
CutSelectionToNewLayerCommand,
} from "../commands/LassoCutoutCommand";
import { OperationType } from "../utils/layerHelper";
const props = defineProps({
@@ -414,7 +418,7 @@ function copySelectionToNewLayer() {
function cutSelectionToNewLayer() {
if (!hasSelection.value) return;
props.commandManager.execute(
new CopySelectionToNewLayerCommand({
new CutSelectionToNewLayerCommand({
canvas: props.canvas,
layerManager: props.layerManager,
selectionManager: props.selectionManager,

View File

@@ -1753,7 +1753,7 @@ export class LayerManager {
* 更新背景图层颜色
* @param {string} backgroundColor 背景颜色
*/
updateBackgroundColor(backgroundColor) {
updateBackgroundColor(backgroundColor, options = {}) {
const backgroundLayer = this.layers.value.find(
(layer) => layer.isBackground
);
@@ -1768,10 +1768,14 @@ export class LayerManager {
canvas: this.canvas,
layers: this.layers,
canvasManager: this.canvasManager,
layerManger: this,
backgroundColor,
backgroundColorValue: this.backgroundColor,
oldColor: options.oldColor,
});
command.undoable = isBoolean(options.undoable) ? options.undoable : true; // 设置为可撤销
// 执行命令
if (this.commandManager) {
this.commandManager.execute(command);

View File

@@ -133,7 +133,7 @@ export class RedGreenModeManager {
this.canvas.on("mouse:up", (event) => {
// 可以在这里添加更多逻辑,比如生成图片或更新状态
nextTick(() => {
requestAnimationFrame(async () => {
requestIdleCallback(async () => {
if (!this.isInitialized) {
console.warn("红绿图模式未初始化,无法处理鼠标事件");
return;

View File

@@ -1,10 +1,8 @@
import { fabric } from "fabric-with-all";
import { generateId } from "../../utils/helper";
import { OperationType } from "../../utils/layerHelper";
import {
ClearSelectionCommand,
CreateSelectionCommand,
} from "../../commands/SelectionCommands";
import { CreateSelectionCommand } from "../../commands/SelectionCommands";
import { ClearSelectionCommand } from "../../commands/LassoCutoutCommand";
/**
* 选区管理器