Files
aida_front/src/component/Canvas/CanvasEditor/commands/RasterizeLayerCommand.js
2025-07-24 21:49:29 +08:00

652 lines
19 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 { Command } from "./Command";
import { fabric } from "fabric-with-all";
import {
LayerType,
OperationType,
createLayer,
findLayerRecursively,
} from "../utils/layerHelper";
import {
generateId,
optimizeCanvasRendering,
findObjectById,
removeCanvasObjectByObject,
} from "../utils/helper";
import { createRasterizedImage } from "../utils/rasterizedImage";
import { message } from "ant-design-vue";
import { restoreFabricObject } from "../utils/objectHelper";
/**
* 组合图层命令
* 将图层中的所有矢量对象转换为位图图像
* 支持普通图层和组图层的组合
*/
export class RasterizeLayerCommand extends Command {
constructor(options) {
super({
name: "组合图层",
saveState: true,
});
this.canvas = options.canvas;
this.layers = options.layers;
this.layerId = options.layerId; // 指定要组合的图层ID
// 是否包含锁定对象
this.hasLocked = options.hasLocked || true;
// 是否包含隐藏对象
this.hasHidden = options.hasHidden || false;
this.activeLayerId = options.activeLayerId;
this.layerManager = options.layerManager;
// 查找目标图层
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
this.layer = layer;
this.parentLayer = parent;
// 新增:如果有父图层,则栅格化父图层及其所有子图层
if (this.parentLayer) {
this.layer = this.parentLayer;
this.layerId = this.parentLayer.id;
}
this.isGroupLayer = this.layer?.children && this.layer.children.length > 0;
if (!this.layer) {
throw new Error(`图层 ${this.layerId} 不存在`);
}
// 检查是否可以组合
if (this.layer.isBackground || this.layer.isFixed) {
throw new Error("背景图层和固定图层不能组合");
}
// 生成新图层ID
this.rasterizedLayerId = generateId("rasterized_layer_");
this.resterizedId = generateId("rasterized_");
this.rasterizedLayer = null;
// 要组合的图层和对象
this.layersToRasterize = [];
this.objectsToRasterize = [];
// 在构造函数中收集相关对象和保存状态
this._initializeCommand();
}
async execute() {
if (!this.layer) {
throw new Error(`图层 ${this.layerId} 不存在`);
}
if (this.objectsToRasterize.length === 0) {
throw new Error("图层没有内容可组合");
}
try {
this.canvas.discardActiveObject();
this.canvas.renderAll();
// 检查是否有遮罩对象
const maskObject = await this._getMaskObject();
// 创建组合图像
const rasterizedImage = await createRasterizedImage({
canvas: this.canvas,
fabricObjects: this.objectsToRasterize,
maskObject: maskObject,
preserveOriginalQuality: true, // 保持原始质量
isGroupWithMask: this.isGroupLayer && !!maskObject, // 标记是否为带遮罩的组
});
// 创建新的组合图层并替换原图层
await this._createRasterizedLayer(rasterizedImage);
// 切换到选择工具
this.layerManager?.toolManager?.setTool?.(OperationType.SELECT);
console.log(`✅ 图层 ${this.layer.name} 组合完成`);
this.canvas?.thumbnailManager?.generateLayerThumbnail?.(
this.rasterizedLayerId
);
this.layerManager.activeLayerId.value = this.rasterizedLayerId;
return this.rasterizedLayerId;
} catch (error) {
console.error("组合图层失败:", error);
throw error;
}
}
async undo() {
if (!this.originalObjectStates || this.originalObjectStates.size === 0) {
throw new Error("没有可恢复的原始数据");
}
try {
await optimizeCanvasRendering(this.canvas, async () => {
// 移除组合后的图像对象
if (this.rasterizedImage) {
removeCanvasObjectByObject(this.canvas, this.rasterizedImage);
}
// 恢复原始对象
this.originalObjectStates.forEach((originalState, objectId) => {
const { object } = findObjectById(this.canvas, objectId);
if (object) {
// 恢复对象状态
object.set(originalState);
object.setCoords();
} else {
// 如果对象不在画布上,重新添加
const originalObject = originalState._fabricObject;
if (originalObject) {
this.canvas.add(originalObject);
originalObject.setCoords();
}
}
});
// 恢复原始图层结构
this.layers.value = [...this.originalLayerStructure];
// 恢复原活动图层
this.activeLayerId.value = this.layerId;
// 更新画布交互性
await this.layerManager?.updateLayersObjectsInteractivity(false);
});
console.log(`↩️ 图层 ${this.layer.name} 组合已撤销`);
return true;
} catch (error) {
console.error("撤销组合失败:", error);
throw error;
}
}
/**
* 初始化命令,收集相关图层和对象,保存原始状态
* @private
*/
_initializeCommand() {
// 收集要组合的图层和对象
this._collectLayersAndObjects();
// 保存原始图层结构(深拷贝相关部分)
this._saveOriginalLayerStructure();
// 保存原始对象状态
this._saveOriginalObjectStates();
}
/**
* 保存原始图层结构
* @private
*/
_saveOriginalLayerStructure() {
// 只保存相关的图层结构,而不是整个图层数组
this.originalLayerStructure = JSON.parse(JSON.stringify(this.layers.value));
}
/**
* 保存原始对象状态
* @private
*/
_saveOriginalObjectStates() {
this.originalObjectStates = new Map();
this.objectsToRasterize.forEach((object) => {
if (object && object.id) {
// 深拷贝对象的重要属性
const originalState = {
left: object.left,
top: object.top,
scaleX: object.scaleX,
scaleY: object.scaleY,
angle: object.angle,
flipX: object.flipX,
flipY: object.flipY,
opacity: object.opacity,
originX: object.originX,
originY: object.originY,
layerId: object.layerId,
layerName: object.layerName,
width: object.width,
height: object.height,
strokeWidth: object.strokeWidth,
visible: object.visible,
// 保存对象引用以便撤销时重新添加
_fabricObject: object,
};
this.originalObjectStates.set(object.id, originalState);
}
});
console.log(`💾 保存了 ${this.originalObjectStates.size} 个对象的原始状态`);
}
/**
* 收集要组合的图层和对象
* @private
*/
_collectLayersAndObjects() {
if (this.isGroupLayer) {
// 组图层:收集自身和所有子图层
this.layersToRasterize = this._collectLayersToRasterize(this.layer);
} else {
// 普通图层:只收集自身
this.layersToRasterize = [this.layer];
}
// 收集所有图层的fabricObjects并按画布z-index顺序排序
const allCanvasObjects = this.canvas.getObjects();
const objectsWithZIndex = [];
this.layersToRasterize.forEach((layer) => {
// if (layer.fill) {
// const { object } = findObjectById(this.canvas, layer.fill.id);
// if (object) {
// // 获取对象在画布中的z-index数组索引
// const zIndex = allCanvasObjects.indexOf(object);
// objectsWithZIndex.push({
// object: object,
// zIndex: zIndex,
// layerObj: layer.fill,
// });
// }
// }
if (layer.fabricObjects && layer.fabricObjects.length > 0) {
layer.fabricObjects.forEach((layerObj) => {
if (layerObj && layerObj.id) {
const { object } = findObjectById(this.canvas, layerObj.id);
if (object) {
// 获取对象在画布中的z-index数组索引
const zIndex = allCanvasObjects.indexOf(object);
objectsWithZIndex.push({
object: object,
zIndex: zIndex,
layerObj: layerObj,
});
}
}
});
}
});
// 按z-index排序确保保持原有的渲染顺序
objectsWithZIndex.sort((a, b) => a.zIndex - b.zIndex);
// 提取排序后的对象
this.objectsToRasterize = objectsWithZIndex.map((item) => item.object);
console.log(
`📊 收集到 ${this.layersToRasterize.length} 个图层,${this.objectsToRasterize.length} 个对象进行组合`
);
console.log(
"🔢 对象z-index顺序:",
objectsWithZIndex.map((item) => ({
id: item.object.id,
type: item.object.type,
zIndex: item.zIndex,
}))
);
}
/**
* 收集要组合的图层(递归收集子图层)
* @param {Object} sourceLayer 源图层
* @returns {Array} 图层数组
* @private
*/
_collectLayersToRasterize(sourceLayer) {
const result = [sourceLayer];
// 如果是组图层,收集所有子图层
if (sourceLayer.children && sourceLayer.children.length > 0) {
sourceLayer.children.forEach((childLayer) => {
if (childLayer) {
result.push(...this._collectLayersToRasterize(childLayer));
}
});
}
return result;
}
/**
* 创建组合图层并替换原图层
* @param {fabric.Image} rasterizedImage 组合后的图像
* @private
*/
async _createRasterizedLayer(rasterizedImage) {
// 保存组合图像的引用用于撤销
this.rasterizedImage = rasterizedImage;
// 从画布中移除原有对象
this.objectsToRasterize.forEach((obj) => {
removeCanvasObjectByObject(this.canvas, obj);
});
// 添加组合图像到画布
this.canvas.add(rasterizedImage);
this.canvas.setActiveObject(rasterizedImage);
// 创建新的组合图层
this.rasterizedLayer = createLayer({
id: this.rasterizedLayerId,
name: `${this.layer.name} (组合)`,
type: LayerType.BITMAP,
visible: this.layer.visible,
locked: this.layer.locked,
opacity: this.layer.opacity,
fabricObjects: [rasterizedImage],
});
// 更新图像对象的图层关联
rasterizedImage.set({
id: this.resterizedId,
// type: "image",
layerId: this.rasterizedLayerId,
layerName: this.rasterizedLayer.name,
});
// 在适当位置添加新的组合图层
this._replaceLayerInStructure();
// 设置为活动图层
this.activeLayerId.value = this.rasterizedLayerId;
await this.layerManager?.updateLayersObjectsInteractivity(false);
console.log(`🎨 组合图层 ${this.rasterizedLayer.name} 创建完成`);
}
/**
* 在图层结构中替换原图层
* @private
*/
_replaceLayerInStructure() {
// 1.当前如果是子图层,则插入到子图层的位置
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
let insertIndex = 0;
// 说明是子图层
if (parent) {
this.layers.value.some((l, index) => {
if (l.id === parent.id) {
insertIndex = this.layers.value?.[index].children?.findIndex(
(fItem) => fItem.id === this.layerId
);
return true; // 找到父图层,停止循环
}
return false; // 继续查找
});
} else {
insertIndex = this.layers.value.findIndex((l) => l.id === this.layerId);
}
if (insertIndex !== -1) {
if (parent) {
const pIndex = this.layers.value.findIndex((l) => l.id === parent.id);
this.layers.value[pIndex].children?.splice?.(
insertIndex,
1,
this.rasterizedLayer
);
} else {
this.layers.value.splice(insertIndex, 1, this.rasterizedLayer);
}
} else {
// 2.如果没有找到父图层,则添加到顶层
this.layers.value.unshift(this.rasterizedLayer);
}
}
/**
* 获取遮罩对象
* @returns {Object|null} 遮罩对象或null
* @private
*/
async _getMaskObject() {
// 如果图层有clippingMask获取对应的fabric对象
if (this.layer?.clippingMask) {
const { object: maskObject } = findObjectById(
this.canvas,
this.layer.clippingMask.id
);
if (maskObject) {
console.log(
`📎 找到遮罩对象: ${maskObject.id}, 类型: ${maskObject.type}`
);
return maskObject;
}
return await restoreFabricObject(this.layer.clippingMask);
}
console.log("📎 未找到遮罩对象");
return null;
}
getInfo() {
return {
name: this.name,
originalLayerId: this.layerId,
originalLayerName: this.layer?.name,
rasterizedLayerId: this.rasterizedLayerId,
rasterizedLayerName: this.rasterizedLayer?.name,
isGroupLayer: this.isGroupLayer,
objectCount: this.objectsToRasterize?.length || 0,
};
}
}
/**
* 导出图层命令
* 将图层中的所有矢量对象转换为位图图像
* 支持普通图层和组图层的组合
*/
export class ExportLayerToImageCommand extends Command {
constructor(options) {
super({
name: "导出图层",
saveState: false, // 导出不需要保存状态
});
this.canvas = options.canvas;
this.layers = options.layers;
this.layerId = options.layerId; // 指定要导出的图层ID
// 是否包含锁定对象
this.hasLocked = options.hasLocked || true;
// 是否包含隐藏对象
this.hasHidden = options.hasHidden || false;
this.activeLayerId = options.activeLayerId;
this.layerManager = options.layerManager;
// 查找目标图层
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
this.layer = layer;
this.parentLayer = parent;
this.isGroupLayer = this.layer?.children && this.layer.children.length > 0;
if (!this.layer) {
throw new Error(`图层 ${this.layerId} 不存在`);
}
// 要导出的图层和对象
this.layersToRasterize = [];
this.objectsToRasterize = [];
// 收集相关对象
this._collectLayersAndObjects();
}
async execute() {
if (!this.layer) {
throw new Error(`图层 ${this.layerId} 不存在`);
}
if (this.objectsToRasterize.length === 0) {
message.error("图层没有内容可导出");
throw new Error("图层没有内容可导出");
}
try {
// 保存原始对象状态
this.canvas.discardActiveObject();
this.canvas.renderAll();
// 重新创建遮罩对象
let clippingMaskFabricObject = null;
if (this.layer?.clippingMask) {
// 重新创建遮罩对象
clippingMaskFabricObject = await restoreFabricObject(
this.layer?.clippingMask,
this.canvas
);
clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({
absolutePositioned: true,
});
clippingMaskFabricObject.dirty = true;
clippingMaskFabricObject.setCoords();
}
// 创建图像
const imageBase64 = await createRasterizedImage({
canvas: this.canvas,
fabricObjects: this.objectsToRasterize,
isReturenDataURL: true,
maskObject: clippingMaskFabricObject || null, // 不处理遮罩
});
// 模拟浏览器下载
const link = document.createElement("a");
link.href = imageBase64;
link.download = `${this.layer.name}.png`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
console.log(`✅ 图层 ${this.layer.name} 导出完成`);
return true;
} catch (error) {
console.error("导出图层失败:", error);
throw error;
}
}
async undo() {
// 导出图片不需要撤销操作
}
/**
* 收集要组合的图层和对象
* @private
*/
_collectLayersAndObjects() {
if (this.isGroupLayer) {
// 组图层:收集自身和所有子图层
this.layersToRasterize = this._collectLayersToRasterize(this.layer);
} else {
// 普通图层:只收集自身
this.layersToRasterize = [this.layer];
}
// 收集所有图层的fabricObjects并按画布z-index顺序排序
const allCanvasObjects = this.canvas.getObjects();
const objectsWithZIndex = [];
this.layersToRasterize.forEach((layer) => {
// if (layer.fill) {
// const { object } = findObjectById(this.canvas, layer.fill.id);
// if (object) {
// // 获取对象在画布中的z-index数组索引
// const zIndex = allCanvasObjects.indexOf(object);
// objectsWithZIndex.push({
// object: object,
// zIndex: zIndex,
// layerObj: layer.fill,
// });
// }
// }
if (layer.fabricObjects && layer.fabricObjects.length > 0) {
layer.fabricObjects.forEach((layerObj) => {
if (layerObj && layerObj.id) {
const { object } = findObjectById(this.canvas, layerObj.id);
if (object) {
// 获取对象在画布中的z-index数组索引
const zIndex = allCanvasObjects.indexOf(object);
objectsWithZIndex.push({
object: object,
zIndex: zIndex,
layerObj: layerObj,
});
}
}
});
}
});
// 按z-index排序确保保持原有的渲染顺序
objectsWithZIndex.sort((a, b) => a.zIndex - b.zIndex);
// 提取排序后的对象
this.objectsToRasterize = objectsWithZIndex.map((item) => item.object);
console.log(
`📊 收集到 ${this.layersToRasterize.length} 个图层,${this.objectsToRasterize.length} 个对象进行组合`
);
console.log(
"🔢 对象z-index顺序:",
objectsWithZIndex.map((item) => ({
id: item.object.id,
type: item.object.type,
zIndex: item.zIndex,
}))
);
}
/**
* 收集要组合的图层(递归收集子图层)
* @param {Object} sourceLayer 源图层
* @returns {Array} 图层数组
* @private
*/
_collectLayersToRasterize(sourceLayer) {
const result = [sourceLayer];
// 如果是组图层,收集所有子图层
if (sourceLayer.children && sourceLayer.children.length > 0) {
sourceLayer.children.forEach((childLayer) => {
if (childLayer) {
result.push(...this._collectLayersToRasterize(childLayer));
}
});
}
return result;
}
getInfo() {
return {
name: this.name,
originalLayerId: this.layerId,
originalLayerName: this.layer?.name,
isGroupLayer: this.isGroupLayer,
objectCount: this.objectsToRasterize?.length || 0,
};
}
}