feat: 添加导出图层功能,支持将图层转换为位图图像并下载
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
removeCanvasObjectByObject,
|
||||
} from "../utils/helper";
|
||||
import { createRasterizedImage } from "../utils/rasterizedImage";
|
||||
import { message } from "ant-design-vue";
|
||||
|
||||
/**
|
||||
* 栅格化图层命令
|
||||
@@ -376,3 +377,196 @@ export class RasterizeLayerCommand extends Command {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出图层命令
|
||||
* 将图层中的所有矢量对象转换为位图图像
|
||||
* 支持普通图层和组图层的栅格化
|
||||
*/
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user