feat: 裁剪组裁剪跟随选择组移动

This commit is contained in:
bighuixiang
2025-07-14 01:00:23 +08:00
parent 96e13cb22a
commit 24e9ba8ae5
80 changed files with 2052 additions and 4292 deletions

View File

@@ -86,12 +86,7 @@ export class ExportManager {
* @returns {String} 图片数据URL
* @private
*/
_exportSpecificLayer(
layerId,
expPicType,
isRedGreenMode,
restoreOpacityInRedGreen
) {
_exportSpecificLayer(layerId, expPicType, isRedGreenMode, restoreOpacityInRedGreen) {
if (!this.layerManager) {
throw new Error("图层管理器未初始化");
}
@@ -115,19 +110,11 @@ export class ExportManager {
// 红绿图模式下使用固定尺寸和裁剪
if (isRedGreenMode) {
return this._exportWithRedGreenMode(
objectsToExport,
expPicType,
restoreOpacityInRedGreen
);
return this._exportWithRedGreenMode(objectsToExport, expPicType, restoreOpacityInRedGreen);
}
// 普通模式使用画布尺寸
return this._exportWithCanvasSize(
objectsToExport,
expPicType,
restoreOpacityInRedGreen
);
return this._exportWithCanvasSize(objectsToExport, expPicType, restoreOpacityInRedGreen);
}
/**
@@ -168,19 +155,11 @@ export class ExportManager {
// 红绿图模式下使用固定尺寸和裁剪
if (isRedGreenMode) {
return this._exportWithRedGreenMode(
objectsToExport,
expPicType,
restoreOpacityInRedGreen
);
return this._exportWithRedGreenMode(objectsToExport, expPicType, restoreOpacityInRedGreen);
}
// 普通模式使用画布尺寸
return this._exportWithCanvasSize(
objectsToExport,
expPicType,
restoreOpacityInRedGreen
);
return this._exportWithCanvasSize(objectsToExport, expPicType, restoreOpacityInRedGreen);
}
/**
@@ -215,22 +194,14 @@ export class ExportManager {
// 红绿图模式下使用固定尺寸和裁剪
if (isRedGreenMode) {
return this._exportWithRedGreenMode(
objectsToExport,
expPicType,
restoreOpacityInRedGreen
);
return this._exportWithRedGreenMode(objectsToExport, expPicType, restoreOpacityInRedGreen);
}
let canvasClipPath = this.canvas.clipPath;
if (isCropByBg) {
const cropWidth =
this.canvasManager?.canvasWidth?.value ||
this.canvas?.canvasWidth ||
this.canvas.width;
this.canvasManager?.canvasWidth?.value || this.canvas?.canvasWidth || this.canvas.width;
const cropHeight =
this.canvasManager?.canvasHeight?.value ||
this.canvas?.canvasHeight ||
this.canvas.height;
this.canvasManager?.canvasHeight?.value || this.canvas?.canvasHeight || this.canvas.height;
canvasClipPath = new fabric.Rect({
left: this.canvas.width / 2,
top: this.canvas.height / 2,
@@ -331,27 +302,14 @@ export class ExportManager {
* @returns {String} 图片数据URL
* @private
*/
async _exportObject(
obj,
expPicType,
isRedGreenMode,
restoreOpacityInRedGreen
) {
async _exportObject(obj, expPicType, isRedGreenMode, restoreOpacityInRedGreen) {
// 红绿图模式下使用固定尺寸和裁剪
if (isRedGreenMode) {
return this._exportWithRedGreenMode(
[obj],
expPicType,
restoreOpacityInRedGreen
);
return this._exportWithRedGreenMode([obj], expPicType, restoreOpacityInRedGreen);
}
// 普通模式使用画布尺寸
return this._exportWithCanvasSize(
[obj],
expPicType,
restoreOpacityInRedGreen
);
return this._exportWithCanvasSize([obj], expPicType, restoreOpacityInRedGreen);
}
/**
@@ -374,8 +332,7 @@ export class ExportManager {
if (layerIdArray && !layerIdArray.includes(layer.id)) continue;
// 检查图层类型过滤条件
if (!this._shouldIncludeLayer(layer, isContainBg, isContainFixed))
continue;
if (!this._shouldIncludeLayer(layer, isContainBg, isContainFixed)) continue;
if (layer.visible) {
const layerObjects = this._collectObjectsFromLayer(layer);
@@ -514,21 +471,12 @@ export class ExportManager {
* @returns {String} 图片数据URL
* @private
*/
async _exportWithRedGreenMode(
objectsToExport,
expPicType,
restoreOpacityInRedGreen
) {
async _exportWithRedGreenMode(objectsToExport, expPicType, restoreOpacityInRedGreen) {
// 获取固定图层对象(衣服底图)作为参考
const fixedLayerObject =
this._getFixedLayerObject() ?? this.canvas.clipPath;
const fixedLayerObject = this._getFixedLayerObject() ?? this.canvas.clipPath;
if (!fixedLayerObject) {
console.warn("红绿图模式下未找到固定图层对象,使用画布尺寸");
return this._exportWithCanvasSize(
objectsToExport,
expPicType,
restoreOpacityInRedGreen
);
return this._exportWithCanvasSize(objectsToExport, expPicType, restoreOpacityInRedGreen);
}
// 获取固定图层对象的边界矩形(包含位置、尺寸、缩放等信息)
@@ -565,10 +513,7 @@ export class ExportManager {
// 克隆并添加所有对象到临时画布,需要调整位置相对于固定图层
for (let i = 0; i < objectsToExport.length; i++) {
const obj = objectsToExport[i];
const cloned = await this._cloneObjectForExport(
obj,
restoreOpacityInRedGreen && true
);
const cloned = await this._cloneObjectForExport(obj, restoreOpacityInRedGreen && true);
if (cloned) {
// 调整对象位置:将原画布坐标转换为以固定图层为原点的相对坐标
cloned.set({
@@ -606,12 +551,7 @@ export class ExportManager {
* @returns {String} 图片数据URL
* @private
*/
async _exportWithCanvasSize(
objectsToExport,
expPicType,
restoreOpacityInRedGreen,
maskObject
) {
async _exportWithCanvasSize(objectsToExport, expPicType, restoreOpacityInRedGreen, maskObject) {
// 使用当前画布尺寸
// const canvasWidth =
// this.canvasManager?.canvasWidth?.value || this.canvas.width;
@@ -634,44 +574,44 @@ export class ExportManager {
console.log("导出图片数据URL:", dataURL);
return dataURL;
// 创建与画布相同尺寸的临时画布
const scaleFactor = 2; // 高清导出
const tempCanvas = document.createElement("canvas");
tempCanvas.width = canvasWidth * scaleFactor;
tempCanvas.height = canvasHeight * scaleFactor;
tempCanvas.style.width = canvasWidth + "px";
tempCanvas.style.height = canvasHeight + "px";
// // 创建与画布相同尺寸的临时画布
// const scaleFactor = 2; // 高清导出
// const tempCanvas = document.createElement("canvas");
// tempCanvas.width = canvasWidth * scaleFactor;
// tempCanvas.height = canvasHeight * scaleFactor;
// tempCanvas.style.width = canvasWidth + "px";
// tempCanvas.style.height = canvasHeight + "px";
const tempFabricCanvas = new fabric.StaticCanvas(tempCanvas, {
width: canvasWidth,
height: canvasHeight,
backgroundColor: null,
});
// const tempFabricCanvas = new fabric.StaticCanvas(tempCanvas, {
// width: canvasWidth,
// height: canvasHeight,
// backgroundColor: null,
// });
tempFabricCanvas.enableRetinaScaling = true;
tempFabricCanvas.imageSmoothingEnabled = true;
tempFabricCanvas.setZoom(1);
// tempFabricCanvas.enableRetinaScaling = true;
// tempFabricCanvas.imageSmoothingEnabled = true;
// tempFabricCanvas.setZoom(1);
try {
// 克隆并添加所有对象到临时画布
for (const obj of objectsToExport) {
const cloned = await this._cloneObjectForExport(
obj,
restoreOpacityInRedGreen && false // 普通模式不强制恢复透明度
);
if (cloned) {
tempFabricCanvas.add(cloned);
}
}
// try {
// // 克隆并添加所有对象到临时画布
// for (const obj of objectsToExport) {
// const cloned = await this._cloneObjectForExport(
// obj,
// restoreOpacityInRedGreen && false, // 普通模式不强制恢复透明度
// );
// if (cloned) {
// tempFabricCanvas.add(cloned);
// }
// }
// 渲染画布
tempFabricCanvas.renderAll();
// // 渲染画布
// tempFabricCanvas.renderAll();
// 生成图片
return this._generateHighQualityDataURL(tempCanvas, expPicType);
} finally {
this._cleanupTempCanvas(tempFabricCanvas);
}
// // 生成图片
// return this._generateHighQualityDataURL(tempCanvas, expPicType);
// } finally {
// this._cleanupTempCanvas(tempFabricCanvas);
// }
}
/**
@@ -703,10 +643,7 @@ export class ExportManager {
* @returns {Promise<fabric.Object>} 克隆的对象
* @private
*/
_cloneObjectAsync(
obj,
propertiesToInclude = ["id", "layerId", "layerName", "name"]
) {
_cloneObjectAsync(obj, propertiesToInclude = ["id", "layerId", "layerName", "name"]) {
return new Promise((resolve, reject) => {
if (!obj) {
resolve(null);
@@ -736,11 +673,7 @@ export class ExportManager {
* @returns {Promise<Object>} 克隆的对象
* @private
*/
async _cloneObjectForExport(
obj,
forceRestoreOpacity = false,
removeClipPath = true
) {
async _cloneObjectForExport(obj, forceRestoreOpacity = false, removeClipPath = true) {
if (!obj) return null;
try {
@@ -874,11 +807,7 @@ export class ExportManager {
}
// 克隆对象作为裁剪路径
const clonedClipPath = await this._cloneObjectForExport(
clipObject,
false,
false
);
const clonedClipPath = await this._cloneObjectForExport(clipObject, false, false);
if (!clonedClipPath) {
console.warn("无法克隆裁剪对象");