2025-06-18 11:05:23 +08:00
|
|
|
|
import { isArray } from "lodash-es";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 图层关联工具类
|
|
|
|
|
|
* 提供图层与画布对象关联管理的通用方法
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 构建单个图层与画布对象的关联关系
|
|
|
|
|
|
* @param {Object} layer 图层对象
|
|
|
|
|
|
* @param {Array} canvasObjects 画布对象数组
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function buildLayerAssociations(layer, canvasObjects) {
|
|
|
|
|
|
if (!layer || !canvasObjects || !isArray(canvasObjects)) return;
|
|
|
|
|
|
// 处理单个fabricObject关联
|
|
|
|
|
|
if (layer.fabricObject) {
|
|
|
|
|
|
// 如果图层已经有关联的fabricObject,确保它的layerId和layerName正确
|
|
|
|
|
|
layer.fabricObject =
|
|
|
|
|
|
canvasObjects.find((obj) => obj.id === layer.fabricObject.id) || null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (layer.clippingMask) {
|
|
|
|
|
|
// clippingMask 可能是一个fabricObject或组
|
2025-06-22 13:52:28 +08:00
|
|
|
|
const clippingMaskObj = canvasObjects.find(
|
|
|
|
|
|
(obj) => obj.id === layer.clippingMask.id
|
|
|
|
|
|
);
|
|
|
|
|
|
layer.clippingMask = clippingMaskObj?.toObject?.(["id"]) || null;
|
2025-06-18 11:05:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理多个fabricObjects关联
|
|
|
|
|
|
if (layer.fabricObjects && isArray(layer.fabricObjects)) {
|
|
|
|
|
|
layer.fabricObjects = layer.fabricObjects
|
|
|
|
|
|
.map((fabricObject) => {
|
|
|
|
|
|
// 确保每个fabricObject的layerId和layerName正确
|
|
|
|
|
|
const obj = canvasObjects.find((obj) => obj.id === fabricObject.id);
|
|
|
|
|
|
if (obj) {
|
|
|
|
|
|
obj.layerId = layer.id; // 确保对象的layerId正确
|
|
|
|
|
|
obj.layerName = layer.name; // 确保对象的layerName正确
|
|
|
|
|
|
return obj;
|
|
|
|
|
|
}
|
|
|
|
|
|
return null; // 如果没有找到对象,返回null
|
|
|
|
|
|
})
|
|
|
|
|
|
.filter((obj) => obj !== null); // 过滤掉null值
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 恢复对象与图层的关联关系
|
|
|
|
|
|
* @param {Object} layerManager 图层管理器实例
|
|
|
|
|
|
* @param {Array} canvasObjects 画布对象数组
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function restoreObjectLayerAssociations(layers, canvasObjects) {
|
|
|
|
|
|
if (!layers || !canvasObjects || !isArray(canvasObjects)) return;
|
2025-06-22 13:52:28 +08:00
|
|
|
|
|
2025-06-18 11:05:23 +08:00
|
|
|
|
layers.forEach((layer) => {
|
|
|
|
|
|
buildLayerAssociations(layer, canvasObjects);
|
|
|
|
|
|
// 处理子图层
|
|
|
|
|
|
if (layer?.children?.length) {
|
|
|
|
|
|
restoreObjectLayerAssociations(layer.children, canvasObjects);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 为画布对象设置图层信息
|
|
|
|
|
|
* @param {Object} fabricObject 画布对象
|
|
|
|
|
|
* @param {Object} layer 图层对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function setObjectLayerInfo(fabricObject, layer) {
|
|
|
|
|
|
if (!fabricObject || !layer) return;
|
|
|
|
|
|
|
|
|
|
|
|
fabricObject.layerId = layer.id;
|
|
|
|
|
|
fabricObject.layerName = layer.name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 清除画布对象的图层信息
|
|
|
|
|
|
* @param {Object} fabricObject 画布对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function clearObjectLayerInfo(fabricObject) {
|
|
|
|
|
|
if (!fabricObject) return;
|
|
|
|
|
|
|
|
|
|
|
|
delete fabricObject.layerId;
|
|
|
|
|
|
delete fabricObject.layerName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 验证图层关联关系的完整性
|
|
|
|
|
|
* @param {Object} layerManager 图层管理器实例
|
|
|
|
|
|
* @param {fabric.Canvas} canvas 画布实例
|
|
|
|
|
|
* @returns {Object} 验证结果 { valid: boolean, issues: Array }
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function validateLayerAssociations(layers, canvasObjects) {
|
|
|
|
|
|
const issues = [];
|
|
|
|
|
|
|
|
|
|
|
|
// 检查画布对象是否都有对应的图层
|
|
|
|
|
|
canvasObjects.forEach((obj) => {
|
|
|
|
|
|
if (obj.layerId) {
|
|
|
|
|
|
const layer = layers.find((l) => l.id === obj.layerId);
|
|
|
|
|
|
if (!layer) {
|
|
|
|
|
|
issues.push({
|
|
|
|
|
|
type: "orphaned_object",
|
|
|
|
|
|
objectId: obj.id,
|
|
|
|
|
|
layerId: obj.layerId,
|
|
|
|
|
|
message: `对象 ${obj.id} 关联的图层 ${obj.layerId} 不存在`,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
issues.push({
|
|
|
|
|
|
type: "missing_layer_id",
|
|
|
|
|
|
objectId: obj.id,
|
|
|
|
|
|
message: `对象 ${obj.id} 缺少图层ID关联`,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 检查图层是否都有对应的画布对象
|
|
|
|
|
|
layers.forEach((layer) => {
|
|
|
|
|
|
if (layer.fabricObject && layer.fabricObject.id) {
|
|
|
|
|
|
const obj = canvasObjects.find((o) => o.id === layer.fabricObject.id);
|
|
|
|
|
|
if (!obj) {
|
|
|
|
|
|
issues.push({
|
|
|
|
|
|
type: "missing_object",
|
|
|
|
|
|
layerId: layer.id,
|
|
|
|
|
|
objectId: layer.fabricObject.id,
|
|
|
|
|
|
message: `图层 ${layer.id} 关联的对象 ${layer.fabricObject.id} 不存在于画布中`,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (layer.fabricObjects && isArray(layer.fabricObjects)) {
|
|
|
|
|
|
layer.fabricObjects.forEach((fabricObj) => {
|
|
|
|
|
|
if (fabricObj.id) {
|
|
|
|
|
|
const obj = canvasObjects.find((o) => o.id === fabricObj.id);
|
|
|
|
|
|
if (!obj) {
|
|
|
|
|
|
issues.push({
|
|
|
|
|
|
type: "missing_object",
|
|
|
|
|
|
layerId: layer.id,
|
|
|
|
|
|
objectId: fabricObj.id,
|
|
|
|
|
|
message: `图层 ${layer.id} 关联的对象 ${fabricObj.id} 不存在于画布中`,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
valid: issues.length === 0,
|
|
|
|
|
|
issues,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 简化layers对象属性,只保留必要的属性
|
2025-06-22 13:52:28 +08:00
|
|
|
|
* @param {Array} layers 图层数组
|
|
|
|
|
|
* @returns {Array} 简化后的图层数组
|
2025-06-18 11:05:23 +08:00
|
|
|
|
*/
|
|
|
|
|
|
export function simplifyLayers(layers) {
|
|
|
|
|
|
if (!layers || !isArray(layers)) {
|
|
|
|
|
|
console.warn("simplifyLayers 请传入有效的图层数组:", layers);
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-22 13:52:28 +08:00
|
|
|
|
return layers.map((layer) => {
|
|
|
|
|
|
const simplifiedLayer = {
|
|
|
|
|
|
id: layer.id,
|
|
|
|
|
|
name: layer.name,
|
|
|
|
|
|
visible: layer.visible,
|
|
|
|
|
|
locked: layer.locked,
|
|
|
|
|
|
opacity: layer.opacity,
|
|
|
|
|
|
isBackground: layer.isBackground || false,
|
|
|
|
|
|
isFixed: layer.isFixed || false,
|
|
|
|
|
|
clippingMask: layer.clippingMask
|
|
|
|
|
|
? {
|
|
|
|
|
|
id: layer.clippingMask.id,
|
|
|
|
|
|
type: layer.clippingMask.type,
|
|
|
|
|
|
}
|
|
|
|
|
|
: null,
|
|
|
|
|
|
fabricObject: layer.fabricObject
|
|
|
|
|
|
? {
|
|
|
|
|
|
id: layer.fabricObject.id,
|
|
|
|
|
|
type: layer.fabricObject.type,
|
|
|
|
|
|
}
|
|
|
|
|
|
: null,
|
|
|
|
|
|
fabricObjects:
|
|
|
|
|
|
layer.fabricObjects && isArray(layer.fabricObjects)
|
|
|
|
|
|
? layer.fabricObjects
|
|
|
|
|
|
.map((fabricObject) =>
|
|
|
|
|
|
fabricObject?.id
|
|
|
|
|
|
? {
|
|
|
|
|
|
id: fabricObject.id,
|
|
|
|
|
|
type: fabricObject.type,
|
|
|
|
|
|
}
|
|
|
|
|
|
: null
|
|
|
|
|
|
)
|
|
|
|
|
|
.filter((obj) => obj !== null)
|
|
|
|
|
|
: [],
|
|
|
|
|
|
children:
|
|
|
|
|
|
layer.children && isArray(layer.children)
|
|
|
|
|
|
? simplifyLayers(layer.children)
|
|
|
|
|
|
: [],
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return simplifiedLayer;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 恢复图层的完整关联关系
|
|
|
|
|
|
* @param {Array} simplifiedLayers 简化的图层数组
|
|
|
|
|
|
* @param {Array} canvasObjects 画布对象数组
|
|
|
|
|
|
* @returns {Array} 恢复关联后的图层数组
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function restoreLayers(simplifiedLayers, canvasObjects) {
|
|
|
|
|
|
if (!simplifiedLayers || !isArray(simplifiedLayers)) {
|
|
|
|
|
|
console.warn("restoreLayers 请传入有效的简化图层数组:", simplifiedLayers);
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!canvasObjects || !isArray(canvasObjects)) {
|
|
|
|
|
|
console.warn("restoreLayers 请传入有效的画布对象数组:", canvasObjects);
|
|
|
|
|
|
return simplifiedLayers;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return simplifiedLayers.map((layer) => {
|
|
|
|
|
|
const restoredLayer = { ...layer };
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复clippingMask关联
|
|
|
|
|
|
if (layer.clippingMask?.id) {
|
|
|
|
|
|
const clippingMaskObj = canvasObjects.find(
|
|
|
|
|
|
(obj) => obj.id === layer.clippingMask.id
|
|
|
|
|
|
);
|
|
|
|
|
|
restoredLayer.clippingMask = clippingMaskObj || null;
|
2025-06-18 11:05:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-22 13:52:28 +08:00
|
|
|
|
// 恢复单个fabricObject关联
|
|
|
|
|
|
if (layer.fabricObject?.id) {
|
|
|
|
|
|
const fabricObj = canvasObjects.find(
|
|
|
|
|
|
(obj) => obj.id === layer.fabricObject.id
|
|
|
|
|
|
);
|
|
|
|
|
|
if (fabricObj) {
|
|
|
|
|
|
fabricObj.layerId = layer.id;
|
|
|
|
|
|
fabricObj.layerName = layer.name;
|
|
|
|
|
|
restoredLayer.fabricObject = fabricObj;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
restoredLayer.fabricObject = null;
|
|
|
|
|
|
}
|
2025-06-18 11:05:23 +08:00
|
|
|
|
}
|
2025-06-22 13:52:28 +08:00
|
|
|
|
|
|
|
|
|
|
// 恢复多个fabricObjects关联
|
2025-06-18 11:05:23 +08:00
|
|
|
|
if (layer.fabricObjects && isArray(layer.fabricObjects)) {
|
2025-06-22 13:52:28 +08:00
|
|
|
|
restoredLayer.fabricObjects = layer.fabricObjects
|
|
|
|
|
|
.map((fabricRef) => {
|
|
|
|
|
|
const fabricObj = canvasObjects.find(
|
|
|
|
|
|
(obj) => obj.id === fabricRef.id
|
|
|
|
|
|
);
|
|
|
|
|
|
if (fabricObj) {
|
|
|
|
|
|
fabricObj.layerId = layer.id;
|
|
|
|
|
|
fabricObj.layerName = layer.name;
|
|
|
|
|
|
return fabricObj;
|
|
|
|
|
|
}
|
|
|
|
|
|
return null;
|
2025-06-18 11:05:23 +08:00
|
|
|
|
})
|
|
|
|
|
|
.filter((obj) => obj !== null);
|
|
|
|
|
|
}
|
2025-06-22 13:52:28 +08:00
|
|
|
|
|
|
|
|
|
|
// 递归处理子图层
|
2025-06-18 11:05:23 +08:00
|
|
|
|
if (layer.children && isArray(layer.children)) {
|
2025-06-22 13:52:28 +08:00
|
|
|
|
restoredLayer.children = restoreLayers(layer.children, canvasObjects);
|
2025-06-18 11:05:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-22 13:52:28 +08:00
|
|
|
|
return restoredLayer;
|
2025-06-18 11:05:23 +08:00
|
|
|
|
});
|
2025-06-22 13:52:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 序列化图层数据用于保存
|
|
|
|
|
|
* @param {Array} layers 图层数组
|
|
|
|
|
|
* @returns {string} 序列化后的JSON字符串
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function serializeLayers(layers) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const simplified = simplifyLayers(layers);
|
|
|
|
|
|
return JSON.stringify(simplified, null, 2);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("序列化图层数据失败:", error);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 反序列化图层数据并恢复关联
|
|
|
|
|
|
* @param {string} serializedLayers 序列化的图层JSON字符串
|
|
|
|
|
|
* @param {Array} canvasObjects 画布对象数组
|
|
|
|
|
|
* @returns {Array} 恢复关联后的图层数组
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function deserializeLayers(serializedLayers, canvasObjects) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const simplified = JSON.parse(serializedLayers);
|
|
|
|
|
|
return restoreLayers(simplified, canvasObjects);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("反序列化图层数据失败:", error);
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取图层的存储快照(用于撤销/重做)
|
|
|
|
|
|
* @param {Array} layers 图层数组
|
|
|
|
|
|
* @returns {Object} 图层快照对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function createLayerSnapshot(layers) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
|
data: simplifyLayers(layers),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 从快照恢复图层状态
|
|
|
|
|
|
* @param {Object} snapshot 图层快照对象
|
|
|
|
|
|
* @param {Array} canvasObjects 画布对象数组
|
|
|
|
|
|
* @returns {Array} 恢复的图层数组
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function restoreFromSnapshot(snapshot, canvasObjects) {
|
|
|
|
|
|
if (!snapshot?.data) {
|
|
|
|
|
|
console.warn("无效的图层快照:", snapshot);
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
2025-06-18 11:05:23 +08:00
|
|
|
|
|
2025-06-22 13:52:28 +08:00
|
|
|
|
return restoreLayers(snapshot.data, canvasObjects);
|
2025-06-18 11:05:23 +08:00
|
|
|
|
}
|