From 3ff5e27db61d4b734a7f1c61bf313a529d79831d Mon Sep 17 00:00:00 2001 From: bighuixiang <472705331@qq.com> Date: Thu, 24 Jul 2025 21:37:21 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E7=BB=86=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FillGroupLayerBackgroundCommand.js | 2 - .../commands/FillLayerBackgroundCommand.js | 42 ++++-- .../commands/ObjectLayerCommands.js | 2 +- .../commands/RasterizeLayerCommand.js | 48 +++++-- .../CanvasEditor/components/LiquifyPanel.vue | 122 ++++++++++++---- src/component/Canvas/CanvasEditor/index.vue | 29 ++-- .../CanvasEditor/managers/LayerManager.js | 5 +- .../CanvasEditor/managers/ToolManager.js | 26 +++- .../liquify/EnhancedLiquifyManager.js | 133 ++++++++++++++---- .../Canvas/CanvasEditor/utils/layerHelper.js | 28 +++- 10 files changed, 335 insertions(+), 102 deletions(-) diff --git a/src/component/Canvas/CanvasEditor/commands/FillGroupLayerBackgroundCommand.js b/src/component/Canvas/CanvasEditor/commands/FillGroupLayerBackgroundCommand.js index 3b450e4d..cf5c8f89 100644 --- a/src/component/Canvas/CanvasEditor/commands/FillGroupLayerBackgroundCommand.js +++ b/src/component/Canvas/CanvasEditor/commands/FillGroupLayerBackgroundCommand.js @@ -72,7 +72,6 @@ export class FillGroupLayerBackgroundCommand extends Command { originY: clippingMaskFabricObject.originY || "center", scaleX: clippingMaskFabricObject.scaleX || 1, scaleY: clippingMaskFabricObject.scaleY || 1, - // type: "fill", }); // this.newFill.clipPath = clippingMaskFabricObject; // this.newFill.dirty = true; @@ -92,7 +91,6 @@ export class FillGroupLayerBackgroundCommand extends Command { evented: false, originX: "center", originY: "center", - // type: "fill", }); } } diff --git a/src/component/Canvas/CanvasEditor/commands/FillLayerBackgroundCommand.js b/src/component/Canvas/CanvasEditor/commands/FillLayerBackgroundCommand.js index 47cb3ee0..2ff386fd 100644 --- a/src/component/Canvas/CanvasEditor/commands/FillLayerBackgroundCommand.js +++ b/src/component/Canvas/CanvasEditor/commands/FillLayerBackgroundCommand.js @@ -52,7 +52,10 @@ export class FillLayerBackgroundCommand extends Command { let clippingMaskFabricObject = null; if (layer.clippingMask) { // 反序列化 clippingMask - clippingMaskFabricObject = await restoreFabricObject(layer.clippingMask, this.canvas); + clippingMaskFabricObject = await restoreFabricObject( + layer.clippingMask, + this.canvas + ); clippingMaskFabricObject.clipPath = null; clippingMaskFabricObject.set({ @@ -75,7 +78,6 @@ export class FillLayerBackgroundCommand extends Command { originY: clippingMaskFabricObject.originY || "center", scaleX: clippingMaskFabricObject.scaleX || 1, scaleY: clippingMaskFabricObject.scaleY || 1, - type: "fill", }); this.newFill.clipPath = clippingMaskFabricObject; // 设置填充的遮罩 @@ -96,7 +98,6 @@ export class FillLayerBackgroundCommand extends Command { evented: false, originX: "center", originY: "center", - type: "fill", }); } } @@ -104,11 +105,15 @@ export class FillLayerBackgroundCommand extends Command { // 判断fabricObjects是否是组,是组则添加填充到最前面,否则创建组 if (layer.fabricObjects && layer.fabricObjects.length > 0) { let insertIndex = - this.canvas.getObjects()?.findIndex((obj) => obj.id === layer.fabricObjects?.[0]?.id) || 0; + this.canvas + .getObjects() + ?.findIndex((obj) => obj.id === layer.fabricObjects?.[0]?.id) || 0; if (this.oldFill) { // 如果有旧填充,先获取旧的索引再移除旧填充 insertIndex = - this.canvas.getObjects()?.findIndex((obj) => obj.id === this.oldFill?.id) || 0; + this.canvas + .getObjects() + ?.findIndex((obj) => obj.id === this.oldFill?.id) || 0; removeCanvasObjectByObject(this.canvas, this.oldFill); } insertIndex = insertIndex == -1 ? 0 : insertIndex; @@ -122,13 +127,17 @@ export class FillLayerBackgroundCommand extends Command { .getObjects() .findIndex( (obj) => - obj.id === this.originalfabricObjects[this.originalfabricObjects.length - 1]?.id + obj.id === + this.originalfabricObjects[this.originalfabricObjects.length - 1] + ?.id ) || 0; if (this.oldFill) { // 如果有旧填充,先获取旧的索引再移除旧填充 insertIndex = - this.canvas.getObjects()?.findIndex((obj) => obj.id === this.oldFill?.id) || 0; + this.canvas + .getObjects() + ?.findIndex((obj) => obj.id === this.oldFill?.id) || 0; removeCanvasObjectByObject(this.canvas, this.oldFill); } insertIndex = insertIndex == -1 ? 0 : insertIndex; @@ -142,7 +151,9 @@ export class FillLayerBackgroundCommand extends Command { if (!this.isRetimeUpdate) { layer.fill = this.newFill.toObject(["id", "layerId"]); layer.fillColor = this.fillColor; - this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layer.id); + this.canvasManager.thumbnailManager?.generateLayerThumbnail( + this.layer.id + ); // 重新排序 // 使用LayerSort工具重新排列画布对象(如果可用) @@ -187,7 +198,10 @@ export class FillLayerBackgroundCommand extends Command { .map((obj) => { return findObjectById(this.canvas.value, obj.id)?.object || obj; }); - } else if (this.layer.fabricObjects && this.layer.fabricObjects.length > 0) { + } else if ( + this.layer.fabricObjects && + this.layer.fabricObjects.length > 0 + ) { // 如果是普通图层,直接返回其fabric对象 return this.layer.fabricObjects.map((obj) => { return findObjectById(this.canvas.value, obj.id)?.object || obj; @@ -204,11 +218,17 @@ export class FillLayerBackgroundCommand extends Command { minTop = Infinity, maxRight = -Infinity, maxBottom = -Infinity; - console.log("计算当前所有对象的边界信息:===>", this.originalfabricObjects.length); + console.log( + "计算当前所有对象的边界信息:===>", + this.originalfabricObjects.length + ); this.originalfabricObjects?.forEach((obj) => { const { object } = findObjectById(this.canvas, obj.id) || {}; if (object) { - const rect = object.getBoundingRect({ absolute: true, includeStroke: false }); + const rect = object.getBoundingRect({ + absolute: true, + includeStroke: false, + }); minLeft = Math.min(minLeft, rect.left); minTop = Math.min(minTop, rect.top); maxRight = Math.max(maxRight, rect.left + rect.width); diff --git a/src/component/Canvas/CanvasEditor/commands/ObjectLayerCommands.js b/src/component/Canvas/CanvasEditor/commands/ObjectLayerCommands.js index 70e8c89b..d9b60f4a 100644 --- a/src/component/Canvas/CanvasEditor/commands/ObjectLayerCommands.js +++ b/src/component/Canvas/CanvasEditor/commands/ObjectLayerCommands.js @@ -224,7 +224,7 @@ export class AddObjectToLayerCommand extends Command { isLocked: layer.locked || false, // 是否锁定 isVisible: layer.visible !== false, // 是否可见 eraser: layer.eraser || false, // 是否为橡皮擦 - type: this.fabricObject.type || "object", // 对象类型 + // type: this.fabricObject.type || "object", // 对象类型 selectable: true, // 设置对象可选择 evented: true, // 设置对象可事件 }); diff --git a/src/component/Canvas/CanvasEditor/commands/RasterizeLayerCommand.js b/src/component/Canvas/CanvasEditor/commands/RasterizeLayerCommand.js index aaf72373..6c58116e 100644 --- a/src/component/Canvas/CanvasEditor/commands/RasterizeLayerCommand.js +++ b/src/component/Canvas/CanvasEditor/commands/RasterizeLayerCommand.js @@ -1,6 +1,11 @@ import { Command } from "./Command"; import { fabric } from "fabric-with-all"; -import { LayerType, OperationType, createLayer, findLayerRecursively } from "../utils/layerHelper"; +import { + LayerType, + OperationType, + createLayer, + findLayerRecursively, +} from "../utils/layerHelper"; import { generateId, optimizeCanvasRendering, @@ -34,7 +39,10 @@ export class RasterizeLayerCommand extends Command { this.layerManager = options.layerManager; // 查找目标图层 - const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId); + 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; @@ -94,7 +102,9 @@ export class RasterizeLayerCommand extends Command { console.log(`✅ 图层 ${this.layer.name} 组合完成`); - this.canvas?.thumbnailManager?.generateLayerThumbnail?.(this.rasterizedLayerId); + this.canvas?.thumbnailManager?.generateLayerThumbnail?.( + this.rasterizedLayerId + ); return this.rasterizedLayerId; } catch (error) { console.error("组合图层失败:", error); @@ -330,7 +340,7 @@ export class RasterizeLayerCommand extends Command { // 更新图像对象的图层关联 rasterizedImage.set({ id: this.resterizedId, - type: "image", + // type: "image", layerId: this.rasterizedLayerId, layerName: this.rasterizedLayer.name, }); @@ -352,7 +362,10 @@ export class RasterizeLayerCommand extends Command { */ _replaceLayerInStructure() { // 1.当前如果是子图层,则插入到子图层的位置 - const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId); + const { layer, parent } = findLayerRecursively( + this.layers.value, + this.layerId + ); let insertIndex = 0; // 说明是子图层 @@ -373,7 +386,11 @@ export class RasterizeLayerCommand extends Command { 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); + this.layers.value[pIndex].children?.splice?.( + insertIndex, + 1, + this.rasterizedLayer + ); } else { this.layers.value.splice(insertIndex, 1, this.rasterizedLayer); } @@ -391,9 +408,14 @@ export class RasterizeLayerCommand extends Command { async _getMaskObject() { // 如果图层有clippingMask,获取对应的fabric对象 if (this.layer?.clippingMask) { - const { object: maskObject } = findObjectById(this.canvas, this.layer.clippingMask.id); + const { object: maskObject } = findObjectById( + this.canvas, + this.layer.clippingMask.id + ); if (maskObject) { - console.log(`📎 找到遮罩对象: ${maskObject.id}, 类型: ${maskObject.type}`); + console.log( + `📎 找到遮罩对象: ${maskObject.id}, 类型: ${maskObject.type}` + ); return maskObject; } return await restoreFabricObject(this.layer.clippingMask); @@ -439,7 +461,10 @@ export class ExportLayerToImageCommand extends Command { this.layerManager = options.layerManager; // 查找目标图层 - const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId); + 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; @@ -475,7 +500,10 @@ export class ExportLayerToImageCommand extends Command { let clippingMaskFabricObject = null; if (this.layer?.clippingMask) { // 重新创建遮罩对象 - clippingMaskFabricObject = await restoreFabricObject(this.layer?.clippingMask, this.canvas); + clippingMaskFabricObject = await restoreFabricObject( + this.layer?.clippingMask, + this.canvas + ); clippingMaskFabricObject.clipPath = null; clippingMaskFabricObject.set({ diff --git a/src/component/Canvas/CanvasEditor/components/LiquifyPanel.vue b/src/component/Canvas/CanvasEditor/components/LiquifyPanel.vue index 77a4096d..8010a385 100644 --- a/src/component/Canvas/CanvasEditor/components/LiquifyPanel.vue +++ b/src/component/Canvas/CanvasEditor/components/LiquifyPanel.vue @@ -310,7 +310,11 @@ async function prepareForLiquify(targetObj) { } // 获取图层信息 - const layerId = targetObj.layerId || props.canvas.activeLayerId?.value; + const layerId = + targetObj.layerId || + canvasManager?.layerManager?.activeLayerId?.value || + canvasManager.activeLayerId?.value || + props.canvas.activeLayerId?.value; targetLayerId.value = layerId; try { @@ -374,7 +378,10 @@ onMounted(() => { }); // 创建状态管理器 - stateManager.value = new LiquifyStateManager(props.canvas, realtimeUpdater.value); + stateManager.value = new LiquifyStateManager( + props.canvas, + realtimeUpdater.value + ); } // 监听画布事件 @@ -443,7 +450,12 @@ function showPanel(event) { return; } - console.log("显示液化面板,目标对象:", targetObj.type, "图层ID:", targetLayerIdValue); + console.log( + "显示液化面板,目标对象:", + targetObj.type, + "图层ID:", + targetLayerIdValue + ); // 更新面板状态,但保持当前模式不变 // 只有在首次显示面板或面板已关闭时才重置模式 @@ -513,8 +525,8 @@ function showPanel(event) { const status = props.liquifyManager.getStatus ? props.liquifyManager.getStatus() : props.liquifyManager.enhancedManager - ? props.liquifyManager.enhancedManager.getStatus() - : {}; + ? props.liquifyManager.enhancedManager.getStatus() + : {}; webglAvailable.value = status.isWebGLAvailable || false; @@ -545,8 +557,8 @@ function showPanel(event) { const status = props.liquifyManager.getStatus ? props.liquifyManager.getStatus() : props.liquifyManager.enhancedManager - ? props.liquifyManager.enhancedManager.getStatus() - : {}; + ? props.liquifyManager.enhancedManager.getStatus() + : {}; webglAvailable.value = status.isWebGLAvailable || false; @@ -605,7 +617,11 @@ function setTargetObject(obj) { // 确保对象有唯一ID if (!obj.id && !obj.objectId && !obj.uid) { - obj.id = "liquify_target_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9); + obj.id = + "liquify_target_" + + Date.now() + + "_" + + Math.random().toString(36).substr(2, 9); } targetObject.value = obj; @@ -673,7 +689,9 @@ function updateParam(paramName, value) { `set${paramName.charAt(0).toUpperCase() + paramName.slice(1)}` ] === "function" ) { - props.liquifyManager[`set${paramName.charAt(0).toUpperCase() + paramName.slice(1)}`](value); + props.liquifyManager[ + `set${paramName.charAt(0).toUpperCase() + paramName.slice(1)}` + ](value); } else { console.warn(`❌ 液化管理器不支持设置参数: ${paramName}`); } @@ -756,9 +774,19 @@ async function getCurrentImageData(targetObject) { tempCtx.drawImage(element, 0, 0, tempCanvas.width, tempCanvas.height); // 获取ImageData - const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height); + const imageData = tempCtx.getImageData( + 0, + 0, + tempCanvas.width, + tempCanvas.height + ); - console.log("✅ 成功获取当前图像状态,尺寸:", imageData.width, "x", imageData.height); + console.log( + "✅ 成功获取当前图像状态,尺寸:", + imageData.width, + "x", + imageData.height + ); return imageData; } catch (error) { console.warn("获取当前图像数据失败:", error); @@ -837,7 +865,9 @@ async function handleMouseDown(event) { const imageCoords = _convertFabricCoordsToImageCoords(pointer.x, pointer.y); if (imageCoords) { props.liquifyManager.startLiquifyOperation(imageCoords.x, imageCoords.y); - console.log(`开始液化操作,图像坐标: (${imageCoords.x}, ${imageCoords.y})`); + console.log( + `开始液化操作,图像坐标: (${imageCoords.x}, ${imageCoords.y})` + ); } } @@ -933,15 +963,27 @@ async function handleMouseUp() { // 尝试从实时更新器获取当前图像数据 if (realtimeUpdater.value) { try { - const currentImageElement = realtimeUpdater.value.getTargetObject()?._element; + const currentImageElement = + realtimeUpdater.value.getTargetObject()?._element; if (currentImageElement) { // 从当前图像元素创建ImageData const tempCanvas = document.createElement("canvas"); tempCanvas.width = initialImageData.value.width; tempCanvas.height = initialImageData.value.height; const tempCtx = tempCanvas.getContext("2d"); - tempCtx.drawImage(currentImageElement, 0, 0, tempCanvas.width, tempCanvas.height); - finalImageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height); + tempCtx.drawImage( + currentImageElement, + 0, + 0, + tempCanvas.width, + tempCanvas.height + ); + finalImageData = tempCtx.getImageData( + 0, + 0, + tempCanvas.width, + tempCanvas.height + ); console.log( "✅ 从实时更新器获取最终图像数据成功,尺寸:", @@ -962,8 +1004,19 @@ async function handleMouseUp() { tempCanvas.width = initialImageData.value.width; tempCanvas.height = initialImageData.value.height; const tempCtx = tempCanvas.getContext("2d"); - tempCtx.drawImage(currentTarget._element, 0, 0, tempCanvas.width, tempCanvas.height); - finalImageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height); + tempCtx.drawImage( + currentTarget._element, + 0, + 0, + tempCanvas.width, + tempCanvas.height + ); + finalImageData = tempCtx.getImageData( + 0, + 0, + tempCanvas.width, + tempCanvas.height + ); console.log( "✅ 从目标对象获取最终图像数据成功,尺寸:", @@ -1048,7 +1101,14 @@ async function applyLiquifyAtPoint(x, y) { return; } - console.log("原始坐标:", x, y, "转换后图像坐标:", imageCoords.x, imageCoords.y); + console.log( + "原始坐标:", + x, + y, + "转换后图像坐标:", + imageCoords.x, + imageCoords.y + ); // 获取当前参数 const params = { @@ -1113,7 +1173,8 @@ async function applyLiquifyAtPoint(x, y) { targetObject.value = updatedObject; // 如果对象有新的ID,也要更新ID if (updatedObject.id || updatedObject.objectId || updatedObject.uid) { - targetObjectId.value = updatedObject.id || updatedObject.objectId || updatedObject.uid; + targetObjectId.value = + updatedObject.id || updatedObject.objectId || updatedObject.uid; } } } @@ -1127,7 +1188,8 @@ async function applyLiquifyAtPoint(x, y) { if (avgOperationTime.value === 0) { avgOperationTime.value = operationTime; } else { - avgOperationTime.value = 0.7 * avgOperationTime.value + 0.3 * operationTime; + avgOperationTime.value = + 0.7 * avgOperationTime.value + 0.3 * operationTime; } // 将性能指标传递给状态管理器 @@ -1267,7 +1329,9 @@ async function _replaceTargetWithNewImage(dataURL) { if (layer.type === "background" || layer.isBackground) { layer.fabricObject = img; } else if (layer.fabricObjects) { - const objIndex = layer.fabricObjects.findIndex((obj) => obj.id === img.id); + const objIndex = layer.fabricObjects.findIndex( + (obj) => obj.id === img.id + ); if (objIndex !== -1) { layer.fabricObjects[objIndex] = img; } @@ -1304,7 +1368,10 @@ function _convertFabricCoordsToImageCoords(fabricX, fabricY) { const matrix = fabric.util.invertTransform(transform); // 应用逆变换,将画布坐标转换为对象本地坐标 - const localPoint = fabric.util.transformPoint(new fabric.Point(fabricX, fabricY), matrix); + const localPoint = fabric.util.transformPoint( + new fabric.Point(fabricX, fabricY), + matrix + ); // 获取图像的原始尺寸(未缩放前) const imageWidth = originalImageData.value.width; @@ -1340,7 +1407,12 @@ function _convertFabricCoordsToImageCoords(fabricX, fabricY) { ); // 检查坐标是否在图像范围内 - if (imageX < 0 || imageX >= imageWidth || imageY < 0 || imageY >= imageHeight) { + if ( + imageX < 0 || + imageX >= imageWidth || + imageY < 0 || + imageY >= imageHeight + ) { console.warn( `坐标超出图像范围: (${imageX}, ${imageY}), 图像尺寸: ${imageWidth}x${imageHeight}` ); @@ -1539,9 +1611,7 @@ function stopPressTimer() { .fade-enter-active, .fade-leave-active { - transition: - opacity 0.3s, - transform 0.3s; + transition: opacity 0.3s, transform 0.3s; } .fade-enter-from, .fade-leave-to { diff --git a/src/component/Canvas/CanvasEditor/index.vue b/src/component/Canvas/CanvasEditor/index.vue index f9f21490..82d16919 100644 --- a/src/component/Canvas/CanvasEditor/index.vue +++ b/src/component/Canvas/CanvasEditor/index.vue @@ -218,6 +218,7 @@ onMounted(async () => { isFixedErasable: props.isFixedErasable, }); canvasManager.canvas.activeLayerId = activeLayerId; + canvasManager.activeLayerId = activeLayerId; canvasManager.canvas.activeElementId = activeElementId; // 创建命令管理器 @@ -1207,8 +1208,8 @@ defineExpose({ bottom: 1rem; right: 1rem; background: rgba(255, 255, 255, 0.7); - padding: .5rem 1rem; - border-radius: .4rem; + padding: 0.5rem 1rem; + border-radius: 0.4rem; font-size: 1.4rem; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); color: #666; @@ -1220,8 +1221,8 @@ defineExpose({ top: 1rem; left: 1rem; background: rgba(255, 255, 255, 0.8); - padding: .5rem 1rem; - border-radius: .4rem; + padding: 0.5rem 1rem; + border-radius: 0.4rem; font-size: 1.2rem; color: #666; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); @@ -1230,11 +1231,11 @@ defineExpose({ .reset-zoom { margin-left: 1rem; cursor: pointer; - padding: .2rem .5rem; + padding: 0.2rem 0.5rem; font-size: 1.2rem; border: 1px solid #ddd; background: #f8f8f8; - border-radius: .3rem; + border-radius: 0.3rem; } .footer-actions { @@ -1247,7 +1248,7 @@ defineExpose({ .share-btn, .export-btn { - padding: .8rem 2rem; + padding: 0.8rem 2rem; border: none; border-radius: 2rem; background-color: #000; @@ -1310,7 +1311,7 @@ button:hover { max-height: 90vh; overflow-y: auto; background-color: #fff; - border-radius: .8rem; + border-radius: 0.8rem; padding: 2rem; } @@ -1337,11 +1338,11 @@ button:hover { right: 1rem; background: rgba(255, 255, 255, 0.9); padding: 1rem; - border-radius: .4rem; + border-radius: 0.4rem; box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2); display: flex; flex-direction: column; - gap: .8rem; + gap: 0.8rem; z-index: 5; } @@ -1353,7 +1354,7 @@ button:hover { background: #fff; width: 35rem; max-height: 85vh; - box-shadow: 0 .4rem 2rem rgba(0, 0, 0, 0.1); /* 添加阴影效果 */ + box-shadow: 0 0.4rem 2rem rgba(0, 0, 0, 0.1); /* 添加阴影效果 */ backdrop-filter: blur(2px); /* 添加模糊效果 */ -webkit-backdrop-filter: blur(2px); background-color: rgba(255, 255, 255, 0.95); /* 改为白色背景 */ @@ -1363,8 +1364,8 @@ button:hover { &::before { content: ""; position: absolute; - top: -.9rem; - right: .6rem; + top: -0.9rem; + right: 0.6rem; width: 0; height: 0; border-left: 1rem solid transparent; @@ -1388,7 +1389,7 @@ button:hover { .layer-item, .element-item { - padding: 1.2rem .8rem; + padding: 1.2rem 0.8rem; } .element-action-btn, diff --git a/src/component/Canvas/CanvasEditor/managers/LayerManager.js b/src/component/Canvas/CanvasEditor/managers/LayerManager.js index f7c7b9f6..57587c59 100644 --- a/src/component/Canvas/CanvasEditor/managers/LayerManager.js +++ b/src/component/Canvas/CanvasEditor/managers/LayerManager.js @@ -903,7 +903,10 @@ export class LayerManager { * @returns {Object|null} 图层对象或null */ getLayerById(layerId) { - const { layer } = findLayerRecursively(this.layers.value, layerId); + const { layer } = findLayerRecursively( + this.layers?.value ?? this.layers, + layerId + ); return layer; } diff --git a/src/component/Canvas/CanvasEditor/managers/ToolManager.js b/src/component/Canvas/CanvasEditor/managers/ToolManager.js index 116a0d83..aa357dd6 100644 --- a/src/component/Canvas/CanvasEditor/managers/ToolManager.js +++ b/src/component/Canvas/CanvasEditor/managers/ToolManager.js @@ -3,7 +3,7 @@ import { BrushManager } from "./brushes/brushManager"; import { BrushIndicator } from "./BrushIndicator"; import { ToolCommand } from "../commands/ToolCommands"; import { CreateTextCommand } from "../commands/TextCommands"; -import { OperationType } from "../utils/layerHelper"; +import { findLayerRecursively, OperationType } from "../utils/layerHelper"; import CanvasConfig from "../config/canvasConfig"; import { fabric } from "fabric-with-all"; import { InitLiquifyToolCommand } from "../commands/LiquifyCommands"; @@ -349,6 +349,11 @@ export class ToolManager { // 保存先前的工具 this.previousTool = this.activeTool.value; + // 取消画布的选中状态 + this.canvas?.discardActiveObject(); + this.canvasManager?.layerManager?.updateLayersObjectsInteractivity?.(); + this.canvas?.renderAll(); + // 如果切换到非画笔工具,禁用笔刷指示器 if (!this._shouldShowBrushIndicator(toolId)) { this._disableBrushIndicator(); @@ -665,7 +670,11 @@ export class ToolManager { panelDetail.layerStatus = checkResult; // 获取图层对象 - const layer = this.layerManager.getLayerById(activeLayerId); + // const layer = this.layerManager.getLayerById(activeLayerId); + const { layer } = findLayerRecursively( + this.layerManager.layers?.value ?? this.layerManager.layers, + activeLayerId + ); // 检查图层是否为空 if (!checkResult.isEmpty) { @@ -844,7 +853,12 @@ export class ToolManager { */ async _startLiquify(layerId) { // 获取图层信息 - const layer = this.layerManager.getLayerById(layerId); + + // const layer = this.layerManager.getLayerById(layerId); + const { layer } = findLayerRecursively( + this.layerManager.layers?.value ?? this.layerManager.layers, + layerId + ); if (!layer) { Modal.error({ title: "图层错误", @@ -1122,7 +1136,11 @@ export class ToolManager { target.type === "textbox") ) { // 获取对应的图层 - const layer = this.layerManager.getLayerById(target.layerId); + // const layer = this.layerManager.getLayerById(target.layerId); + const { layer } = findLayerRecursively( + this.layerManager.layers?.value ?? this.layerManager.layers, + target.layerId + ); if (layer) { // 显示文本编辑面板 this.showTextEditor(target, layer); diff --git a/src/component/Canvas/CanvasEditor/managers/liquify/EnhancedLiquifyManager.js b/src/component/Canvas/CanvasEditor/managers/liquify/EnhancedLiquifyManager.js index 5d7dd281..13a6015e 100644 --- a/src/component/Canvas/CanvasEditor/managers/liquify/EnhancedLiquifyManager.js +++ b/src/component/Canvas/CanvasEditor/managers/liquify/EnhancedLiquifyManager.js @@ -4,7 +4,7 @@ */ import { LiquifyWebGLManager } from "./LiquifyWebGLManager"; import { LiquifyCPUManager } from "./LiquifyCPUManager"; -import { findInChildLayers, LayerType } from "../../utils/layerHelper"; +import { findLayerRecursively, LayerType } from "../../utils/layerHelper"; export class EnhancedLiquifyManager { /** @@ -140,7 +140,10 @@ export class EnhancedLiquifyManager { // 处理传入的是图层ID的情况 if (typeof target === "string") { targetLayerId = target; - const layer = this.layerManager.getLayerById(targetLayerId); + const { layer } = findLayerRecursively( + this.layerManager.layers?.value ?? this.layerManager.layers, + targetLayerId + ); // 检查图层是否存在和是否有对象 let hasObjects = false; @@ -160,7 +163,7 @@ export class EnhancedLiquifyManager { } else if (typeof target === "object") { // 传入的是对象 targetObject = target; - const { layer } = findInChildLayers( + const { layer } = findLayerRecursively( this.layerManager.layers?.value ?? this.layerManager.layers, targetObject.layerId ); @@ -216,7 +219,9 @@ export class EnhancedLiquifyManager { // 计算图像大小 const pixelCount = imageData.width * imageData.height; - console.log(`液化选择渲染器: 图像大小=${pixelCount}像素, WebGL可用=${this.isWebGLAvailable}`); + console.log( + `液化选择渲染器: 图像大小=${pixelCount}像素, WebGL可用=${this.isWebGLAvailable}` + ); // 默认使用CPU渲染器 this.activeRenderer = this.cpuRenderer; @@ -247,7 +252,11 @@ export class EnhancedLiquifyManager { this.activeRenderer = this.webglRenderer; this.renderMode = "webgl"; } else { - console.log(`液化功能: 使用CPU渲染模式${!this.isWebGLAvailable ? " (WebGL不可用)" : ""}`); + console.log( + `液化功能: 使用CPU渲染模式${ + !this.isWebGLAvailable ? " (WebGL不可用)" : "" + }` + ); } } @@ -312,11 +321,16 @@ export class EnhancedLiquifyManager { this.params[param] = value; // 同步更新当前渲染器 - 关键修复:确保参数正确传递 - if (this.activeRenderer && typeof this.activeRenderer.setParam === "function") { + if ( + this.activeRenderer && + typeof this.activeRenderer.setParam === "function" + ) { console.log(`EnhancedLiquifyManager 设置参数: ${param}=${value}`); this.activeRenderer.setParam(param, value); } else { - console.warn(`EnhancedLiquifyManager: 无法设置参数 ${param},渲染器未就绪`); + console.warn( + `EnhancedLiquifyManager: 无法设置参数 ${param},渲染器未就绪` + ); } return true; @@ -371,17 +385,25 @@ export class EnhancedLiquifyManager { * @param {Number} y 初始Y坐标 */ startLiquifyOperation(x, y) { - if (this.activeRenderer && typeof this.activeRenderer.startDeformation === "function") { + if ( + this.activeRenderer && + typeof this.activeRenderer.startDeformation === "function" + ) { this.activeRenderer.startDeformation(x, y); } - console.log(`开始液化操作,渲染模式=${this.renderMode}, 初始点: (${x}, ${y})`); + console.log( + `开始液化操作,渲染模式=${this.renderMode}, 初始点: (${x}, ${y})` + ); } /** * 结束液化操作 */ endLiquifyOperation() { - if (this.activeRenderer && typeof this.activeRenderer.endDeformation === "function") { + if ( + this.activeRenderer && + typeof this.activeRenderer.endDeformation === "function" + ) { this.activeRenderer.endDeformation(); } console.log(`结束液化操作,渲染模式=${this.renderMode}`); @@ -424,7 +446,9 @@ export class EnhancedLiquifyManager { // 坐标边界检查 if (x < 0 || x >= imageWidth || y < 0 || y >= imageHeight) { - console.warn(`液化坐标超出图像范围: (${x}, ${y}), 图像尺寸: ${imageWidth}x${imageHeight}`); + console.warn( + `液化坐标超出图像范围: (${x}, ${y}), 图像尺寸: ${imageWidth}x${imageHeight}` + ); return null; } @@ -489,7 +513,9 @@ export class EnhancedLiquifyManager { console.log( `液化性能数据: 模式=${this.renderMode}, 平均耗时=${avgTime.toFixed( 2 - )}ms, 图像尺寸=${this.originalImageData?.width}x${this.originalImageData?.height}` + )}ms, 图像尺寸=${this.originalImageData?.width}x${ + this.originalImageData?.height + }` ); } @@ -542,7 +568,11 @@ export class EnhancedLiquifyManager { } // 获取图层 - const layer = this.layerManager.getLayerById(layerId); + // const layer = this.layerManager.getLayerById(layerId); + const { layer } = findLayerRecursively( + this.layerManager.layers?.value ?? this.layerManager.layers, + layerId + ); if (!layer) { return { valid: false, @@ -581,7 +611,8 @@ export class EnhancedLiquifyManager { const singleObject = objectsToCheck.length === 1; const isImage = singleObject && - (objectsToCheck[0].type === "image" || objectsToCheck[0].type === "rasterized-layer"); + (objectsToCheck[0].type === "image" || + objectsToCheck[0].type === "rasterized-layer"); // 检查是否为组 const isGroup = @@ -631,14 +662,19 @@ export class EnhancedLiquifyManager { tempCanvas.height = fabricObject.height; const tempCtx = tempCanvas.getContext("2d"); - console.log(`创建临时Canvas,尺寸: ${tempCanvas.width}x${tempCanvas.height}`); + console.log( + `创建临时Canvas,尺寸: ${tempCanvas.width}x${tempCanvas.height}` + ); // 处理不同的图像源 if (fabricObject._element) { console.log("使用 _element 绘制图像"); // 检查_element是否有效 - if (!fabricObject._element.complete && fabricObject._element.tagName === "IMG") { + if ( + !fabricObject._element.complete && + fabricObject._element.tagName === "IMG" + ) { console.log("图像未加载完成,等待加载..."); fabricObject._element.onload = () => { try { @@ -649,7 +685,12 @@ export class EnhancedLiquifyManager { fabricObject.width, fabricObject.height ); - const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height); + const imageData = tempCtx.getImageData( + 0, + 0, + tempCanvas.width, + tempCanvas.height + ); console.log("✅ 图像加载完成后获取数据成功"); resolve(imageData); } catch (error) { @@ -664,8 +705,17 @@ export class EnhancedLiquifyManager { } // 直接绘制已加载的图像 - tempCtx.drawImage(fabricObject._element, 0, 0, fabricObject.width, fabricObject.height); - } else if (fabricObject.getSrc && typeof fabricObject.getSrc === "function") { + tempCtx.drawImage( + fabricObject._element, + 0, + 0, + fabricObject.width, + fabricObject.height + ); + } else if ( + fabricObject.getSrc && + typeof fabricObject.getSrc === "function" + ) { console.log("使用 getSrc() 方法获取图像源"); // 通过URL创建图像 @@ -674,11 +724,24 @@ export class EnhancedLiquifyManager { img.onload = () => { try { - console.log(`图像加载成功,原始尺寸: ${img.naturalWidth}x${img.naturalHeight}`); + console.log( + `图像加载成功,原始尺寸: ${img.naturalWidth}x${img.naturalHeight}` + ); - tempCtx.drawImage(img, 0, 0, fabricObject.width, fabricObject.height); + tempCtx.drawImage( + img, + 0, + 0, + fabricObject.width, + fabricObject.height + ); - const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height); + const imageData = tempCtx.getImageData( + 0, + 0, + tempCanvas.width, + tempCanvas.height + ); console.log("✅ 通过URL获取图像数据成功"); resolve(imageData); @@ -706,9 +769,20 @@ export class EnhancedLiquifyManager { img.onload = () => { try { - tempCtx.drawImage(img, 0, 0, fabricObject.width, fabricObject.height); + tempCtx.drawImage( + img, + 0, + 0, + fabricObject.width, + fabricObject.height + ); - const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height); + const imageData = tempCtx.getImageData( + 0, + 0, + tempCanvas.width, + tempCanvas.height + ); console.log("✅ 通过src属性获取图像数据成功"); resolve(imageData); @@ -728,13 +802,20 @@ export class EnhancedLiquifyManager { return; } else { console.error("无法找到有效的图像源"); - reject(new Error("图像对象缺少有效的图像源(_element, getSrc, 或 src)")); + reject( + new Error("图像对象缺少有效的图像源(_element, getSrc, 或 src)") + ); return; } // 如果走到这里,说明使用了_element直接绘制 try { - const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height); + const imageData = tempCtx.getImageData( + 0, + 0, + tempCanvas.width, + tempCanvas.height + ); console.log( `✅ 获取图像数据成功: 对象尺寸=${fabricObject.width}x${fabricObject.height}, ` + diff --git a/src/component/Canvas/CanvasEditor/utils/layerHelper.js b/src/component/Canvas/CanvasEditor/utils/layerHelper.js index 150210b5..0d081e9f 100644 --- a/src/component/Canvas/CanvasEditor/utils/layerHelper.js +++ b/src/component/Canvas/CanvasEditor/utils/layerHelper.js @@ -95,7 +95,8 @@ export const BlendMode = { export function isGroupLayer(layer) { if (!layer) return false; return ( - layer.type === LayerType.GROUP || (Array.isArray(layer.children) && layer.children.length > 0) + layer.type === LayerType.GROUP || + (Array.isArray(layer.children) && layer.children.length > 0) ); } @@ -106,7 +107,11 @@ export function isGroupLayer(layer) { * @param {Object} options 其他选项 * @returns {Object} 创建的图层对象 */ -export function createLayerFromFabricObject(fabricObject, layerType = "bitmap", options = {}) { +export function createLayerFromFabricObject( + fabricObject, + layerType = "bitmap", + options = {} +) { if (!fabricObject) return null; // 确定图层类型 @@ -132,7 +137,9 @@ export function createLayerFromFabricObject(fabricObject, layerType = "bitmap", type: type, name: options.name || - `${fabricObject.type.charAt(0).toUpperCase() + fabricObject.type.slice(1)} 图层`, + `${ + fabricObject.type.charAt(0).toUpperCase() + fabricObject.type.slice(1) + } 图层`, parentId: options.parentId || null, }); @@ -159,7 +166,9 @@ export function createLayerFromFabricObject(fabricObject, layerType = "bitmap", */ export function createLayer(options = {}) { const id = - options.id || generateId("layer_") || `layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; + options.id || + generateId("layer_") || + `layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; return { id: id, // 图层基本属性 @@ -199,7 +208,8 @@ export function createLayer(options = {}) { * @returns {Object} 背景图层对象 */ export function createBackgroundLayer(options = {}) { - const id = options.id || `bg_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; + const id = + options.id || `bg_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; return { id: id, // 图层基本属性 @@ -390,7 +400,9 @@ export function createSmartObjectLayer(options = {}) { * @returns {Object} 固定图层对象 */ export function createFixedLayer(options = {}) { - const id = options.id || `fixed_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; + const id = + options.id || + `fixed_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; return { id: id, // 图层基本属性 @@ -433,7 +445,9 @@ export function cloneLayer(layer) { // 复制 fabric 对象 (如果存在) if (Array.isArray(layer.fabricObjects)) { clonedLayer.fabricObjects = layer.fabricObjects.map((obj) => { - return obj && typeof obj.clone === "function" ? obj.clone() : JSON.parse(JSON.stringify(obj)); + return obj && typeof obj.clone === "function" + ? obj.clone() + : JSON.parse(JSON.stringify(obj)); }); }