画布增加的新功能

This commit is contained in:
李志鹏
2026-01-02 11:24:11 +08:00
parent 1ae365b1f3
commit f8e4ab8cdb
59 changed files with 4401 additions and 1213 deletions

View File

@@ -10,6 +10,7 @@ import { AddObjectToLayerCommand } from "./ObjectLayerCommands";
import { ToolCommand } from "./ToolCommands";
import {
findObjectById,
findObjectByLayerId,
generateId,
getObjectZIndex,
insertObjectAtZIndex,
@@ -19,7 +20,7 @@ import {
} from "../utils/helper";
import { fabric } from "fabric-with-all";
import { restoreFabricObject } from "../utils/objectHelper";
import EventManager from "../utils/event.js";
/**
* 添加图层命令
*/
@@ -36,7 +37,7 @@ export class AddLayerCommand extends Command {
this.insertIndex = options.insertIndex;
this.oldActiveLayerId = null;
this.beforeLayers = [...this.layers.value]; // 备份原图层列表
this.beforeLayers = JSON.stringify(this.layers.value); // 备份原图层列表
this.options = options.options || {};
}
@@ -70,7 +71,7 @@ export class AddLayerCommand extends Command {
undo() {
// 从图层列表删除该图层
this.layers.value = [...this.beforeLayers];
this.layers.value = JSON.parse(this.beforeLayers);
// 恢复原活动图层
this.activeLayerId.value = this.oldActiveLayerId;
@@ -251,12 +252,12 @@ export class PasteLayerCommand extends Command {
(await restoreFabricObject(groupLayer?.clippingMask, this.canvas)) ||
null;
clippingMaskFabricObject.clipPath = null;
// clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({
absolutePositioned: true,
});
clippingMaskFabricObject.dirty = true;
// clippingMaskFabricObject.dirty = true;
clippingMaskFabricObject.setCoords();
// 添加所有对象到画布
allObjects.forEach((obj) => {
@@ -802,15 +803,23 @@ export class ToggleLayerVisibilityCommand extends Command {
// 切换可见性
this.layer.visible = !this.layer.visible;
const ids = [this.layerId];
const childLayers = this.layer?.children || [];
childLayers.forEach((childLayer) => {
childLayer.visible = this.layer.visible;
ids.push(childLayer.id);
});
// 更新画布上图层对象的可见性
if (this.canvas) {
const layerObjects = this.canvas
.getObjects()
.filter((obj) => obj.layerId === this.layerId);
layerObjects.forEach((obj) => {
obj.visible = this.layer.visible;
});
this.canvas.getObjects().forEach((obj) => {
if (ids.includes(obj.layerId)) {
obj.getObjects?.()?.forEach((item) => {
item.visible = this.layer.visible;
});
obj.visible = this.layer.visible;
}
});
}
// 更新画布上对象的可选择状态
await this.layerManager?.updateLayersObjectsInteractivity();
@@ -868,13 +877,14 @@ export class ToggleChildLayerVisibilityCommand extends Command {
// 更新画布上图层对象的可见性
if (this.canvas) {
const layerObjects = this.canvas
.getObjects()
.filter((obj) => obj.layerId === this.layerId);
layerObjects.forEach((obj) => {
obj.visible = this.childLayer.visible;
});
this.canvas.getObjects().forEach((obj) => {
if (obj.layerId === this.layerId) {
obj.getObjects?.()?.forEach((item) => {
item.visible = this.childLayer.visible;
});
obj.visible = this.childLayer.visible;
}
});
}
// 更新画布上对象的可选择状态
@@ -1007,9 +1017,8 @@ export class LayerLockCommand extends Command {
// 如果是组图层,递归更新所有子图层
if (
layer.type === "group" &&
layer.children &&
Array.isArray(layer.children)
Array.isArray(layer.children) && layer.children.length > 0
) {
layer.children.forEach((child) => {
this._updateLayerLockState(child, locked);
@@ -1108,7 +1117,7 @@ export class SetLayerOpacityCommand extends Command {
this.canvas.renderAll();
}
EventManager.emit("object:opacity:execute", this.layerId, this.opacity);
return true;
}
@@ -1130,6 +1139,7 @@ export class SetLayerOpacityCommand extends Command {
this.canvas.renderAll();
}
}
EventManager.emit("object:opacity:undo", this.layerId, this.opacity);
}
getInfo() {
@@ -1371,7 +1381,7 @@ export class GroupLayersCommand extends Command {
// 备份原图层
this.originalLayers = [...this.layers.value];
// 新组ID
this.groupId =
this.groupId = options.id ||
generateId("group_layer_") ||
`group_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
@@ -4434,3 +4444,87 @@ export class ChildLayerLockCommand extends Command {
};
}
}
/**
* 设置图层混合模式
*/
export class SetLayerCompositeCommand extends Command {
constructor(options) {
super({
name: "设置图层混合模式",
saveState: false,
});
this.canvas = options.canvas;
this.layers = options.layers;
this.layerManager = options.layerManager;
this.layerId = options.layerId;
this.newValue = options.newValue;
this.oldValue = options.oldValue;
}
execute(isUndo = false) {
const { layer } = findLayerRecursively(this.layers.value, this.layerId);
const { object } = findObjectByLayerId(this.canvas, this.layerId);
if (!layer || !object) {
console.error(`图层${this.layerId}不存在`);
return false;
}
// console.log("==========", this.newValue, this.oldValue);
const value = isUndo ? this.oldValue : this.newValue;
layer.blendMode = value;
object.set("globalCompositeOperation", value);
this.canvas.renderAll();
const event = isUndo ? "object:composite:undo" : "object:composite:execute";
EventManager.emit(event, object);
return true;
}
undo() {
return this.execute(true);
}
}
/**
* 设置颜色图层颜色
*/
export class SetColorLayerFillCommand extends Command {
constructor(options) {
super({
name: "设置颜色图层颜色",
saveState: false,
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.object = options.object;
this.layer = this.layerManager?.getLayerById(this.object.layerId);
this.newFill = options.newFill;
this.oldFill = JSON.parse(JSON.stringify(this.object.fill));
}
async execute(isUndo = false) {
if (!this.object) {
console.error(`颜色图层不存在`);
return false;
}
const isVisible = this.layer?.visible;
if(!isVisible && this.layer) this.layer.visible = true;
const gradient = new fabric.Gradient({
type: "linear",
gradientUnits: "percentage",
...(isUndo ? this.oldFill : this.newFill),
});
this.object.setFill(gradient);
this.canvas.renderAll();
await this.canvas?.thumbnailManager?.generateLayerThumbnail?.(
this.object.id
);
if(!isVisible && this.layer) this.layer.visible = false;
this.layerManager?.updateLayersObjectsInteractivity();
return true;
}
undo() {
this.execute(true);
return true;
}
}