2025-06-22 13:52:28 +08:00
|
|
|
|
import { Command } from "./Command";
|
|
|
|
|
|
import { fabric } from "fabric-with-all";
|
2025-07-14 01:00:23 +08:00
|
|
|
|
import { LayerType, OperationType, createLayer, findLayerRecursively } from "../utils/layerHelper";
|
2025-06-22 13:52:28 +08:00
|
|
|
|
import {
|
|
|
|
|
|
generateId,
|
|
|
|
|
|
optimizeCanvasRendering,
|
|
|
|
|
|
findObjectById,
|
|
|
|
|
|
removeCanvasObjectByObject,
|
|
|
|
|
|
} from "../utils/helper";
|
|
|
|
|
|
import { createRasterizedImage } from "../utils/rasterizedImage";
|
2025-06-23 15:56:01 +08:00
|
|
|
|
import { message } from "ant-design-vue";
|
2025-07-10 01:01:46 +08:00
|
|
|
|
import { restoreFabricObject } from "../utils/objectHelper";
|
2025-06-22 13:52:28 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 栅格化图层命令
|
|
|
|
|
|
* 将图层中的所有矢量对象转换为位图图像
|
|
|
|
|
|
* 支持普通图层和组图层的栅格化
|
|
|
|
|
|
*/
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
// 查找目标图层
|
2025-07-14 01:00:23 +08:00
|
|
|
|
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId);
|
2025-06-22 13:52:28 +08:00
|
|
|
|
this.layer = layer;
|
|
|
|
|
|
this.parentLayer = parent;
|
|
|
|
|
|
this.isGroupLayer = this.layer?.children && this.layer.children.length > 0;
|
|
|
|
|
|
|
2025-07-08 01:08:45 +08:00
|
|
|
|
if (!this.layer) {
|
|
|
|
|
|
throw new Error(`图层 ${this.layerId} 不存在`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否可以栅格化
|
|
|
|
|
|
if (this.layer.isBackground || this.layer.isFixed) {
|
|
|
|
|
|
throw new Error("背景图层和固定图层不能栅格化");
|
|
|
|
|
|
}
|
2025-06-22 13:52:28 +08:00
|
|
|
|
|
|
|
|
|
|
// 生成新图层ID
|
|
|
|
|
|
this.rasterizedLayerId = generateId("rasterized_layer_");
|
|
|
|
|
|
this.resterizedId = generateId("rasterized_");
|
|
|
|
|
|
this.rasterizedLayer = null;
|
|
|
|
|
|
|
|
|
|
|
|
// 要栅格化的图层和对象
|
|
|
|
|
|
this.layersToRasterize = [];
|
|
|
|
|
|
this.objectsToRasterize = [];
|
2025-07-08 01:08:45 +08:00
|
|
|
|
|
|
|
|
|
|
// 在构造函数中收集相关对象和保存状态
|
|
|
|
|
|
this._initializeCommand();
|
2025-06-22 13:52:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async execute() {
|
|
|
|
|
|
if (!this.layer) {
|
|
|
|
|
|
throw new Error(`图层 ${this.layerId} 不存在`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-08 01:08:45 +08:00
|
|
|
|
if (this.objectsToRasterize.length === 0) {
|
|
|
|
|
|
throw new Error("图层没有内容可栅格化");
|
2025-06-22 13:52:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
this.canvas.discardActiveObject();
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
|
2025-07-08 01:08:45 +08:00
|
|
|
|
// 检查是否有遮罩对象
|
2025-07-10 01:01:46 +08:00
|
|
|
|
const maskObject = await this._getMaskObject();
|
2025-07-08 01:08:45 +08:00
|
|
|
|
|
2025-06-22 13:52:28 +08:00
|
|
|
|
// 创建栅格化图像
|
|
|
|
|
|
const rasterizedImage = await createRasterizedImage({
|
|
|
|
|
|
canvas: this.canvas,
|
|
|
|
|
|
fabricObjects: this.objectsToRasterize,
|
2025-07-08 01:08:45 +08:00
|
|
|
|
maskObject: maskObject,
|
|
|
|
|
|
preserveOriginalQuality: true, // 保持原始质量
|
|
|
|
|
|
isGroupWithMask: this.isGroupLayer && !!maskObject, // 标记是否为带遮罩的组
|
2025-06-22 13:52:28 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 创建新的栅格化图层并替换原图层
|
|
|
|
|
|
await this._createRasterizedLayer(rasterizedImage);
|
|
|
|
|
|
|
|
|
|
|
|
// 切换到选择工具
|
|
|
|
|
|
this.layerManager?.toolManager?.setTool?.(OperationType.SELECT);
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`✅ 图层 ${this.layer.name} 栅格化完成`);
|
|
|
|
|
|
|
2025-07-14 01:00:23 +08:00
|
|
|
|
this.canvas?.thumbnailManager?.generateLayerThumbnail?.(this.rasterizedLayerId);
|
2025-06-22 13:52:28 +08:00
|
|
|
|
return this.rasterizedLayerId;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("栅格化图层失败:", error);
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async undo() {
|
2025-07-08 01:08:45 +08:00
|
|
|
|
if (!this.originalObjectStates || this.originalObjectStates.size === 0) {
|
2025-06-22 13:52:28 +08:00
|
|
|
|
throw new Error("没有可恢复的原始数据");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await optimizeCanvasRendering(this.canvas, async () => {
|
2025-07-08 01:08:45 +08:00
|
|
|
|
// 移除栅格化后的图像对象
|
|
|
|
|
|
if (this.rasterizedImage) {
|
|
|
|
|
|
removeCanvasObjectByObject(this.canvas, this.rasterizedImage);
|
|
|
|
|
|
}
|
2025-06-22 13:52:28 +08:00
|
|
|
|
|
2025-07-08 01:08:45 +08:00
|
|
|
|
// 恢复原始对象
|
|
|
|
|
|
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();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-22 13:52:28 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复原始图层结构
|
2025-07-08 01:08:45 +08:00
|
|
|
|
this.layers.value = [...this.originalLayerStructure];
|
2025-06-22 13:52:28 +08:00
|
|
|
|
|
|
|
|
|
|
// 恢复原活动图层
|
|
|
|
|
|
this.activeLayerId.value = this.layerId;
|
|
|
|
|
|
|
|
|
|
|
|
// 更新画布交互性
|
|
|
|
|
|
await this.layerManager?.updateLayersObjectsInteractivity(false);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`↩️ 图层 ${this.layer.name} 栅格化已撤销`);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("撤销栅格化失败:", error);
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-08 01:08:45 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 初始化命令,收集相关图层和对象,保存原始状态
|
|
|
|
|
|
* @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} 个对象的原始状态`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-22 13:52:28 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 收集要栅格化的图层和对象
|
|
|
|
|
|
* @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.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) {
|
2025-07-08 01:08:45 +08:00
|
|
|
|
// 保存栅格化图像的引用用于撤销
|
|
|
|
|
|
this.rasterizedImage = rasterizedImage;
|
|
|
|
|
|
|
2025-06-22 13:52:28 +08:00
|
|
|
|
// 从画布中移除原有对象
|
|
|
|
|
|
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,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 在适当位置添加新的栅格化图层
|
2025-07-08 01:08:45 +08:00
|
|
|
|
this._replaceLayerInStructure();
|
|
|
|
|
|
|
|
|
|
|
|
// 设置为活动图层
|
|
|
|
|
|
this.activeLayerId.value = this.rasterizedLayerId;
|
|
|
|
|
|
|
|
|
|
|
|
await this.layerManager?.updateLayersObjectsInteractivity(false);
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`🎨 栅格化图层 ${this.rasterizedLayer.name} 创建完成`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 在图层结构中替换原图层
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
_replaceLayerInStructure() {
|
2025-06-22 13:52:28 +08:00
|
|
|
|
// 1.当前如果是子图层,则插入到子图层的位置
|
2025-07-14 01:00:23 +08:00
|
|
|
|
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId);
|
2025-06-22 13:52:28 +08:00
|
|
|
|
|
|
|
|
|
|
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);
|
2025-07-14 01:00:23 +08:00
|
|
|
|
this.layers.value[pIndex].children?.splice?.(insertIndex, 1, this.rasterizedLayer);
|
2025-07-08 01:08:45 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
this.layers.value.splice(insertIndex, 1, this.rasterizedLayer);
|
|
|
|
|
|
}
|
2025-06-22 13:52:28 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// 2.如果没有找到父图层,则添加到顶层
|
|
|
|
|
|
this.layers.value.unshift(this.rasterizedLayer);
|
|
|
|
|
|
}
|
2025-07-08 01:08:45 +08:00
|
|
|
|
}
|
2025-06-22 13:52:28 +08:00
|
|
|
|
|
2025-07-08 01:08:45 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取遮罩对象
|
|
|
|
|
|
* @returns {Object|null} 遮罩对象或null
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
2025-07-10 01:01:46 +08:00
|
|
|
|
async _getMaskObject() {
|
2025-07-08 01:08:45 +08:00
|
|
|
|
// 如果图层有clippingMask,获取对应的fabric对象
|
2025-07-10 01:01:46 +08:00
|
|
|
|
if (this.layer?.clippingMask) {
|
2025-07-14 01:00:23 +08:00
|
|
|
|
const { object: maskObject } = findObjectById(this.canvas, this.layer.clippingMask.id);
|
2025-07-08 01:08:45 +08:00
|
|
|
|
if (maskObject) {
|
2025-07-14 01:00:23 +08:00
|
|
|
|
console.log(`📎 找到遮罩对象: ${maskObject.id}, 类型: ${maskObject.type}`);
|
2025-07-08 01:08:45 +08:00
|
|
|
|
return maskObject;
|
|
|
|
|
|
}
|
2025-07-10 01:01:46 +08:00
|
|
|
|
return await restoreFabricObject(this.layer.clippingMask);
|
2025-07-08 01:08:45 +08:00
|
|
|
|
}
|
2025-06-22 13:52:28 +08:00
|
|
|
|
|
2025-07-08 01:08:45 +08:00
|
|
|
|
console.log("📎 未找到遮罩对象");
|
|
|
|
|
|
return null;
|
2025-06-22 13:52:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-23 15:56:01 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 导出图层命令
|
|
|
|
|
|
* 将图层中的所有矢量对象转换为位图图像
|
|
|
|
|
|
* 支持普通图层和组图层的栅格化
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class ExportLayerToImageCommand extends Command {
|
|
|
|
|
|
constructor(options) {
|
|
|
|
|
|
super({
|
|
|
|
|
|
name: "导出图层",
|
2025-07-08 01:08:45 +08:00
|
|
|
|
saveState: false, // 导出不需要保存状态
|
2025-06-23 15:56:01 +08:00
|
|
|
|
});
|
|
|
|
|
|
this.canvas = options.canvas;
|
|
|
|
|
|
this.layers = options.layers;
|
2025-07-08 01:08:45 +08:00
|
|
|
|
this.layerId = options.layerId; // 指定要导出的图层ID
|
2025-06-23 15:56:01 +08:00
|
|
|
|
// 是否包含锁定对象
|
|
|
|
|
|
this.hasLocked = options.hasLocked || true;
|
|
|
|
|
|
// 是否包含隐藏对象
|
|
|
|
|
|
this.hasHidden = options.hasHidden || false;
|
|
|
|
|
|
|
|
|
|
|
|
this.activeLayerId = options.activeLayerId;
|
|
|
|
|
|
this.layerManager = options.layerManager;
|
|
|
|
|
|
|
|
|
|
|
|
// 查找目标图层
|
2025-07-14 01:00:23 +08:00
|
|
|
|
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId);
|
2025-06-23 15:56:01 +08:00
|
|
|
|
this.layer = layer;
|
|
|
|
|
|
this.parentLayer = parent;
|
|
|
|
|
|
this.isGroupLayer = this.layer?.children && this.layer.children.length > 0;
|
|
|
|
|
|
|
2025-07-08 01:08:45 +08:00
|
|
|
|
if (!this.layer) {
|
|
|
|
|
|
throw new Error(`图层 ${this.layerId} 不存在`);
|
|
|
|
|
|
}
|
2025-06-23 15:56:01 +08:00
|
|
|
|
|
2025-07-08 01:08:45 +08:00
|
|
|
|
// 要导出的图层和对象
|
2025-06-23 15:56:01 +08:00
|
|
|
|
this.layersToRasterize = [];
|
|
|
|
|
|
this.objectsToRasterize = [];
|
2025-07-08 01:08:45 +08:00
|
|
|
|
|
|
|
|
|
|
// 收集相关对象
|
|
|
|
|
|
this._collectLayersAndObjects();
|
2025-06-23 15:56:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async execute() {
|
|
|
|
|
|
if (!this.layer) {
|
|
|
|
|
|
throw new Error(`图层 ${this.layerId} 不存在`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-08 01:08:45 +08:00
|
|
|
|
if (this.objectsToRasterize.length === 0) {
|
|
|
|
|
|
message.error("图层没有内容可导出");
|
|
|
|
|
|
throw new Error("图层没有内容可导出");
|
|
|
|
|
|
}
|
2025-06-23 15:56:01 +08:00
|
|
|
|
|
2025-07-08 01:08:45 +08:00
|
|
|
|
try {
|
2025-06-23 15:56:01 +08:00
|
|
|
|
// 保存原始对象状态
|
|
|
|
|
|
this.canvas.discardActiveObject();
|
|
|
|
|
|
this.canvas.renderAll();
|
|
|
|
|
|
|
|
|
|
|
|
// 创建图像
|
|
|
|
|
|
const imageBase64 = await createRasterizedImage({
|
|
|
|
|
|
canvas: this.canvas,
|
|
|
|
|
|
fabricObjects: this.objectsToRasterize,
|
|
|
|
|
|
isReturenDataURL: true,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 模拟浏览器下载
|
|
|
|
|
|
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.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,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|