421 lines
12 KiB
JavaScript
421 lines
12 KiB
JavaScript
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";
|
|
|
|
/**
|
|
* 创建选区命令
|
|
*/
|
|
export class CreateSelectionCommand extends Command {
|
|
constructor(options = {}) {
|
|
super({
|
|
name: options.name || "创建选区",
|
|
description: "在画布上创建选区",
|
|
saveState: false,
|
|
});
|
|
this.canvas = options.canvas;
|
|
this.selectionManager = options.selectionManager;
|
|
this.selectionObject = options.selectionObject;
|
|
this.selectionType = options.selectionType || "rectangle";
|
|
}
|
|
|
|
async execute() {
|
|
if (!this.canvas || !this.selectionManager || !this.selectionObject) {
|
|
console.error("无法创建选区:参数无效");
|
|
return false;
|
|
}
|
|
|
|
// 将选择对象添加到选区管理器
|
|
this.selectionManager.setSelectionObject(this.selectionObject);
|
|
return true;
|
|
}
|
|
|
|
async undo() {
|
|
if (!this.selectionManager) return false;
|
|
this.selectionManager.clearSelection();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 反转选区命令
|
|
*/
|
|
export class InvertSelectionCommand extends Command {
|
|
constructor(options = {}) {
|
|
super({
|
|
name: "反转选区",
|
|
description: "反转当前选区",
|
|
saveState: false,
|
|
});
|
|
this.canvas = options.canvas;
|
|
this.selectionManager = options.selectionManager;
|
|
this.originalSelection = options.selectionManager
|
|
? options.selectionManager.getSelectionPath()
|
|
: null;
|
|
}
|
|
|
|
async execute() {
|
|
if (!this.canvas || !this.selectionManager) {
|
|
console.error("无法反转选区:参数无效");
|
|
return false;
|
|
}
|
|
|
|
// 保存原始选区
|
|
if (!this.originalSelection) {
|
|
this.originalSelection = this.selectionManager.getSelectionPath();
|
|
}
|
|
|
|
// 反转选区
|
|
const result = await this.selectionManager.invertSelection();
|
|
return result;
|
|
}
|
|
|
|
async undo() {
|
|
if (!this.selectionManager || !this.originalSelection) return false;
|
|
|
|
// 恢复原始选区
|
|
this.selectionManager.setSelectionFromPath(this.originalSelection);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 添加到选区命令
|
|
*/
|
|
export class AddToSelectionCommand extends Command {
|
|
constructor(options = {}) {
|
|
super({
|
|
name: "添加到选区",
|
|
description: "将新的选区添加到现有选区",
|
|
saveState: false,
|
|
});
|
|
this.canvas = options.canvas;
|
|
this.selectionManager = options.selectionManager;
|
|
this.newSelection = options.newSelection;
|
|
this.originalSelection = options.selectionManager
|
|
? options.selectionManager.getSelectionPath()
|
|
: null;
|
|
}
|
|
|
|
async execute() {
|
|
if (!this.canvas || !this.selectionManager || !this.newSelection) {
|
|
console.error("无法添加到选区:参数无效");
|
|
return false;
|
|
}
|
|
|
|
// 保存原始选区
|
|
if (!this.originalSelection) {
|
|
this.originalSelection = this.selectionManager.getSelectionPath();
|
|
}
|
|
|
|
// 添加到选区
|
|
const result = await this.selectionManager.addToSelection(this.newSelection);
|
|
return result;
|
|
}
|
|
|
|
async undo() {
|
|
if (!this.selectionManager || !this.originalSelection) return false;
|
|
|
|
// 恢复原始选区
|
|
this.selectionManager.setSelectionFromPath(this.originalSelection);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 从选区中移除命令
|
|
*/
|
|
export class RemoveFromSelectionCommand extends Command {
|
|
constructor(options = {}) {
|
|
super({
|
|
name: "从选区中移除",
|
|
description: "从现有选区中移除指定区域",
|
|
saveState: false,
|
|
});
|
|
this.canvas = options.canvas;
|
|
this.selectionManager = options.selectionManager;
|
|
this.removeSelection = options.removeSelection;
|
|
this.originalSelection = options.selectionManager
|
|
? options.selectionManager.getSelectionPath()
|
|
: null;
|
|
}
|
|
|
|
async execute() {
|
|
if (!this.canvas || !this.selectionManager || !this.removeSelection) {
|
|
console.error("无法从选区中移除:参数无效");
|
|
return false;
|
|
}
|
|
|
|
// 保存原始选区
|
|
if (!this.originalSelection) {
|
|
this.originalSelection = this.selectionManager.getSelectionPath();
|
|
}
|
|
|
|
// 从选区中移除
|
|
const result = await this.selectionManager.removeFromSelection(this.removeSelection);
|
|
return result;
|
|
}
|
|
|
|
async undo() {
|
|
if (!this.selectionManager || !this.originalSelection) return false;
|
|
|
|
// 恢复原始选区
|
|
this.selectionManager.setSelectionFromPath(this.originalSelection);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 羽化选区命令
|
|
*/
|
|
export class FeatherSelectionCommand extends Command {
|
|
constructor(options = {}) {
|
|
super({
|
|
name: "羽化选区",
|
|
description: "对当前选区应用羽化效果",
|
|
saveState: false,
|
|
});
|
|
this.selectionManager = options.selectionManager;
|
|
this.featherAmount = options.featherAmount || 5;
|
|
this.originalSelection = options.selectionManager
|
|
? options.selectionManager.getSelectionPath()
|
|
: null;
|
|
this.originalFeatherAmount = options.selectionManager
|
|
? options.selectionManager.getFeatherAmount()
|
|
: 0;
|
|
}
|
|
|
|
async execute() {
|
|
if (!this.selectionManager) {
|
|
console.error("无法羽化选区:参数无效");
|
|
return false;
|
|
}
|
|
|
|
// 保存原始选区和羽化值
|
|
if (!this.originalSelection) {
|
|
this.originalSelection = this.selectionManager.getSelectionPath();
|
|
this.originalFeatherAmount = this.selectionManager.getFeatherAmount();
|
|
}
|
|
|
|
// 应用羽化
|
|
const result = await this.selectionManager.featherSelection(this.featherAmount);
|
|
return result;
|
|
}
|
|
|
|
async undo() {
|
|
if (!this.selectionManager || !this.originalSelection) return false;
|
|
|
|
// 恢复原始选区和羽化值
|
|
this.selectionManager.setSelectionFromPath(this.originalSelection);
|
|
this.selectionManager.setFeatherAmount(this.originalFeatherAmount);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 填充选区命令
|
|
*/
|
|
export class FillSelectionCommand extends Command {
|
|
constructor(options = {}) {
|
|
super({
|
|
name: "填充选区",
|
|
description: "使用指定颜色填充当前选区",
|
|
saveState: false,
|
|
});
|
|
this.canvas = options.canvas;
|
|
this.layerManager = options.layerManager;
|
|
this.selectionManager = options.selectionManager;
|
|
this.color = options.color || "#000000";
|
|
this.targetLayerId = options.targetLayerId;
|
|
this.createdObjectIds = [];
|
|
}
|
|
|
|
async execute() {
|
|
if (!this.canvas || !this.layerManager || !this.selectionManager) {
|
|
console.error("无法填充选区:参数无效");
|
|
return false;
|
|
}
|
|
|
|
// 获取选区路径
|
|
const selectionPath = this.selectionManager.getSelectionObject();
|
|
if (!selectionPath) {
|
|
console.error("无法填充选区:当前没有选区");
|
|
return false;
|
|
}
|
|
|
|
// 确定目标图层
|
|
const layerId = this.targetLayerId || this.layerManager.getActiveLayerId();
|
|
if (!layerId) {
|
|
console.error("无法填充选区:没有活动图层");
|
|
return false;
|
|
}
|
|
|
|
// 创建填充对象
|
|
const fillObject = new fabric.Path(selectionPath.path, {
|
|
fill: this.color,
|
|
stroke: null,
|
|
opacity: 1,
|
|
id: `fill_${Date.now()}_${Math.floor(Math.random() * 1000)}`,
|
|
layerId: layerId,
|
|
selectable: false,
|
|
});
|
|
|
|
// 应用羽化效果(如果有)
|
|
const featherAmount = this.selectionManager.getFeatherAmount();
|
|
if (featherAmount > 0) {
|
|
fillObject.shadow = new fabric.Shadow({
|
|
color: this.color,
|
|
blur: featherAmount,
|
|
offsetX: 0,
|
|
offsetY: 0,
|
|
});
|
|
}
|
|
|
|
// 添加到图层
|
|
this.layerManager.addObjectToLayer(layerId, fillObject);
|
|
this.createdObjectIds.push(fillObject.id);
|
|
|
|
// 清空选区
|
|
this.selectionManager.clearSelection();
|
|
return true;
|
|
}
|
|
|
|
async undo() {
|
|
if (!this.layerManager || this.createdObjectIds.length === 0) return false;
|
|
|
|
// 移除创建的填充对象
|
|
for (const id of this.createdObjectIds) {
|
|
const layerObj = this._findObjectInLayers(id);
|
|
if (layerObj) {
|
|
this.layerManager.removeObjectFromLayer(layerObj.layerId, id);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
_findObjectInLayers(objectId) {
|
|
if (!this.layerManager) return null;
|
|
|
|
const layers = this.layerManager.layers.value;
|
|
for (const layer of layers) {
|
|
if (layer.fabricObjects) {
|
|
const obj = layer.fabricObjects.find((obj) => obj.id === objectId);
|
|
if (obj) return { object: obj, layerId: layer.id };
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 复制选区内容到新图层命令
|
|
*/
|
|
export class CopySelectionToNewLayerCommand extends CompositeCommand {
|
|
constructor(options = {}) {
|
|
super([], {
|
|
name: "复制选区到新图层",
|
|
description: "将选区中的内容复制到新图层",
|
|
});
|
|
this.canvas = options.canvas;
|
|
this.layerManager = options.layerManager;
|
|
this.selectionManager = options.selectionManager;
|
|
this.sourceLayerId = options.sourceLayerId;
|
|
this.newLayerName = options.newLayerName || "选区复制";
|
|
this.newLayerId = null;
|
|
this.copiedObjectIds = [];
|
|
}
|
|
|
|
async execute() {
|
|
if (!this.canvas || !this.layerManager || !this.selectionManager) {
|
|
console.error("无法复制选区:参数无效");
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
// 获取选区
|
|
const selectionObject = this.selectionManager.getSelectionObject();
|
|
if (!selectionObject) {
|
|
console.error("无法复制选区:当前没有选区");
|
|
return false;
|
|
}
|
|
|
|
// 确定源图层
|
|
const sourceId = this.sourceLayerId || this.layerManager.getActiveLayerId();
|
|
const sourceLayer = this.layerManager.getLayerById(sourceId);
|
|
if (!sourceLayer || !sourceLayer.fabricObjects) {
|
|
console.error("无法复制选区:源图层无效或为空");
|
|
return false;
|
|
}
|
|
|
|
// 创建新图层
|
|
this.newLayerId = await this.layerManager.createLayer(this.newLayerName, LayerType.EMPTY);
|
|
|
|
// 获取选区内的对象
|
|
const objectsToCopy = sourceLayer.fabricObjects.filter((obj) => {
|
|
return this.selectionManager.isObjectInSelection(obj);
|
|
});
|
|
|
|
if (objectsToCopy.length === 0) {
|
|
console.warn("选区内没有对象可复制");
|
|
return true; // 仍然返回成功,因为已创建了新图层
|
|
}
|
|
|
|
// 复制对象到新图层
|
|
for (const obj of objectsToCopy) {
|
|
// 克隆对象
|
|
const clonedObj = await this._cloneObject(obj);
|
|
// 设置新的ID和图层ID
|
|
const newId = `copy_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
|
clonedObj.id = newId;
|
|
clonedObj.layerId = this.newLayerId;
|
|
|
|
// 添加到新图层
|
|
await this.layerManager.addObjectToLayer(this.newLayerId, clonedObj);
|
|
this.copiedObjectIds.push(newId);
|
|
}
|
|
|
|
// 设置新图层为活动图层
|
|
this.layerManager.setActiveLayer(this.newLayerId);
|
|
|
|
// 清空选区
|
|
this.selectionManager.clearSelection();
|
|
|
|
return {
|
|
newLayerId: this.newLayerId,
|
|
copiedCount: this.copiedObjectIds.length,
|
|
};
|
|
} catch (error) {
|
|
console.error("复制选区过程中出错:", error);
|
|
|
|
// 如果已经创建了新图层,需要进行清理
|
|
if (this.newLayerId) {
|
|
try {
|
|
await this.layerManager.removeLayer(this.newLayerId);
|
|
} catch (cleanupError) {
|
|
console.warn("清理新图层失败:", cleanupError);
|
|
}
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async _cloneObject(obj) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!obj) {
|
|
reject(new Error("对象无效,无法克隆"));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
obj.clone((cloned) => {
|
|
resolve(cloned);
|
|
});
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
}
|
|
}
|