Files
aida_front/src/component/Canvas/CanvasEditor/commands/RasterizeLayerCommand.js

573 lines
16 KiB
JavaScript
Raw Normal View History

2025-06-22 13:52:28 +08:00
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";
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;
// 查找目标图层
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;
// 保存原始状态用于撤销
this.originalLayers = [...this.layers.value];
this.originalCanvasObjects = [...this.canvas.getObjects()];
this.originalObjectStates = new Map();
// 栅格化结果
this.rasterizedImage = null;
this.rasterizedImageId = null;
// 生成新图层ID
this.rasterizedLayerId = generateId("rasterized_layer_");
this.resterizedId = generateId("rasterized_");
this.rasterizedLayer = null;
// 要栅格化的图层和对象
this.layersToRasterize = [];
this.objectsToRasterize = [];
}
async execute() {
// 查找目标图层
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} 不存在`);
}
// 检查是否可以栅格化
if (this.layer.isBackground || this.layer.isFixed) {
throw new Error("背景图层和固定图层不能栅格化");
}
try {
// 收集要栅格化的图层和对象
this._collectLayersAndObjects();
if (this.objectsToRasterize.length === 0) {
throw new Error("图层没有内容可栅格化");
}
// 保存原始对象状态
this._saveOriginalObjectStates();
this.canvas.discardActiveObject();
this.canvas.renderAll();
// 创建栅格化图像
const rasterizedImage = await createRasterizedImage({
canvas: this.canvas,
fabricObjects: this.objectsToRasterize,
});
// 创建新的栅格化图层并替换原图层
await this._createRasterizedLayer(rasterizedImage);
// 切换到选择工具
this.layerManager?.toolManager?.setTool?.(OperationType.SELECT);
console.log(`✅ 图层 ${this.layer.name} 栅格化完成`);
this.canvas?.thumbnailManager?.generateLayerThumbnail?.(
this.rasterizedLayerId
);
return this.rasterizedLayerId;
} catch (error) {
console.error("栅格化图层失败:", error);
throw error;
}
}
async undo() {
if (!this.originalLayers || !this.originalCanvasObjects) {
throw new Error("没有可恢复的原始数据");
}
try {
await optimizeCanvasRendering(this.canvas, async () => {
// 清空画布
this.canvas.discardActiveObject();
this.canvas.clear();
// 恢复原始对象及其状态
this.originalCanvasObjects.forEach((obj) => {
// 如果保存了该对象的原始状态,则恢复状态
if (this.originalObjectStates.has(obj.id)) {
const originalState = this.originalObjectStates.get(obj.id);
obj.set(originalState);
}
this.canvas.add(obj);
obj.setCoords();
});
// 恢复原始图层结构
this.layers.value = [...this.originalLayers];
// 恢复原活动图层
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
*/
_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;
}
/**
* 保存原始对象状态
* @private
*/
_saveOriginalObjectStates() {
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,
};
this.originalObjectStates.set(object.id, originalState);
}
});
}
/**
* 创建栅格化图层并替换原图层
* @param {fabric.Image} rasterizedImage 栅格化后的图像
* @private
*/
async _createRasterizedLayer(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,
});
// 在适当位置添加新的栅格化图层
// 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);
}
// // 替换图层结构
// if (this.isGroupLayer) {
// // 组图层:移除所有相关图层
// const layerIdsToRemove = this.layersToRasterize.map((layer) => layer.id);
// this.layers.value = this.layers.value.filter(
// (layer) => !layerIdsToRemove.includes(layer.id)
// );
// } else {
// // 普通图层:移除原图层
// const layerIndex = this.layers.value.findIndex(
// (l) => l.id === this.layerId
// );
// if (layerIndex !== -1) {
// this.layers.value.splice(layerIndex, 1);
// }
// }
// 设置为活动图层
this.activeLayerId.value = this.rasterizedLayerId;
await this.layerManager?.updateLayersObjectsInteractivity(false);
console.log(`🎨 栅格化图层 ${this.rasterizedLayer.name} 创建完成`);
}
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: 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;
this.isGroupLayer = this.layer?.children && this.layer.children.length > 0;
// 保存原始状态用于撤销
this.originalLayers = [...this.layers.value];
this.originalCanvasObjects = [...this.canvas.getObjects()];
this.originalObjectStates = new Map();
// 栅格化结果
this.rasterizedImage = null;
this.rasterizedImageId = null;
// 生成新图层ID
this.rasterizedLayerId = generateId("rasterized_layer_");
this.resterizedId = generateId("rasterized_");
this.rasterizedLayer = null;
// 要栅格化的图层和对象
this.layersToRasterize = [];
this.objectsToRasterize = [];
}
async execute() {
// 查找目标图层
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} 不存在`);
}
try {
// 收集要栅格化的图层和对象
this._collectLayersAndObjects();
if (this.objectsToRasterize.length === 0) {
message.error("图层没有内容可导出");
throw new Error("图层没有内容可导出");
}
// 保存原始对象状态
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,
rasterizedLayerId: this.rasterizedLayerId,
rasterizedLayerName: this.rasterizedLayer?.name,
isGroupLayer: this.isGroupLayer,
objectCount: this.objectsToRasterize?.length || 0,
};
}
}