fix: 优化细节

This commit is contained in:
bighuixiang
2025-07-24 21:37:21 +08:00
parent 46b1c5cd71
commit 3ff5e27db6
10 changed files with 335 additions and 102 deletions

View File

@@ -72,7 +72,6 @@ export class FillGroupLayerBackgroundCommand extends Command {
originY: clippingMaskFabricObject.originY || "center", originY: clippingMaskFabricObject.originY || "center",
scaleX: clippingMaskFabricObject.scaleX || 1, scaleX: clippingMaskFabricObject.scaleX || 1,
scaleY: clippingMaskFabricObject.scaleY || 1, scaleY: clippingMaskFabricObject.scaleY || 1,
// type: "fill",
}); });
// this.newFill.clipPath = clippingMaskFabricObject; // this.newFill.clipPath = clippingMaskFabricObject;
// this.newFill.dirty = true; // this.newFill.dirty = true;
@@ -92,7 +91,6 @@ export class FillGroupLayerBackgroundCommand extends Command {
evented: false, evented: false,
originX: "center", originX: "center",
originY: "center", originY: "center",
// type: "fill",
}); });
} }
} }

View File

@@ -52,7 +52,10 @@ export class FillLayerBackgroundCommand extends Command {
let clippingMaskFabricObject = null; let clippingMaskFabricObject = null;
if (layer.clippingMask) { if (layer.clippingMask) {
// 反序列化 clippingMask // 反序列化 clippingMask
clippingMaskFabricObject = await restoreFabricObject(layer.clippingMask, this.canvas); clippingMaskFabricObject = await restoreFabricObject(
layer.clippingMask,
this.canvas
);
clippingMaskFabricObject.clipPath = null; clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({ clippingMaskFabricObject.set({
@@ -75,7 +78,6 @@ export class FillLayerBackgroundCommand extends Command {
originY: clippingMaskFabricObject.originY || "center", originY: clippingMaskFabricObject.originY || "center",
scaleX: clippingMaskFabricObject.scaleX || 1, scaleX: clippingMaskFabricObject.scaleX || 1,
scaleY: clippingMaskFabricObject.scaleY || 1, scaleY: clippingMaskFabricObject.scaleY || 1,
type: "fill",
}); });
this.newFill.clipPath = clippingMaskFabricObject; // 设置填充的遮罩 this.newFill.clipPath = clippingMaskFabricObject; // 设置填充的遮罩
@@ -96,7 +98,6 @@ export class FillLayerBackgroundCommand extends Command {
evented: false, evented: false,
originX: "center", originX: "center",
originY: "center", originY: "center",
type: "fill",
}); });
} }
} }
@@ -104,11 +105,15 @@ export class FillLayerBackgroundCommand extends Command {
// 判断fabricObjects是否是组是组则添加填充到最前面否则创建组 // 判断fabricObjects是否是组是组则添加填充到最前面否则创建组
if (layer.fabricObjects && layer.fabricObjects.length > 0) { if (layer.fabricObjects && layer.fabricObjects.length > 0) {
let insertIndex = 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) { if (this.oldFill) {
// 如果有旧填充,先获取旧的索引再移除旧填充 // 如果有旧填充,先获取旧的索引再移除旧填充
insertIndex = 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); removeCanvasObjectByObject(this.canvas, this.oldFill);
} }
insertIndex = insertIndex == -1 ? 0 : insertIndex; insertIndex = insertIndex == -1 ? 0 : insertIndex;
@@ -122,13 +127,17 @@ export class FillLayerBackgroundCommand extends Command {
.getObjects() .getObjects()
.findIndex( .findIndex(
(obj) => (obj) =>
obj.id === this.originalfabricObjects[this.originalfabricObjects.length - 1]?.id obj.id ===
this.originalfabricObjects[this.originalfabricObjects.length - 1]
?.id
) || 0; ) || 0;
if (this.oldFill) { if (this.oldFill) {
// 如果有旧填充,先获取旧的索引再移除旧填充 // 如果有旧填充,先获取旧的索引再移除旧填充
insertIndex = 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); removeCanvasObjectByObject(this.canvas, this.oldFill);
} }
insertIndex = insertIndex == -1 ? 0 : insertIndex; insertIndex = insertIndex == -1 ? 0 : insertIndex;
@@ -142,7 +151,9 @@ export class FillLayerBackgroundCommand extends Command {
if (!this.isRetimeUpdate) { if (!this.isRetimeUpdate) {
layer.fill = this.newFill.toObject(["id", "layerId"]); layer.fill = this.newFill.toObject(["id", "layerId"]);
layer.fillColor = this.fillColor; layer.fillColor = this.fillColor;
this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layer.id); this.canvasManager.thumbnailManager?.generateLayerThumbnail(
this.layer.id
);
// 重新排序 // 重新排序
// 使用LayerSort工具重新排列画布对象如果可用 // 使用LayerSort工具重新排列画布对象如果可用
@@ -187,7 +198,10 @@ export class FillLayerBackgroundCommand extends Command {
.map((obj) => { .map((obj) => {
return findObjectById(this.canvas.value, obj.id)?.object || 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对象 // 如果是普通图层直接返回其fabric对象
return this.layer.fabricObjects.map((obj) => { return this.layer.fabricObjects.map((obj) => {
return findObjectById(this.canvas.value, obj.id)?.object || obj; return findObjectById(this.canvas.value, obj.id)?.object || obj;
@@ -204,11 +218,17 @@ export class FillLayerBackgroundCommand extends Command {
minTop = Infinity, minTop = Infinity,
maxRight = -Infinity, maxRight = -Infinity,
maxBottom = -Infinity; maxBottom = -Infinity;
console.log("计算当前所有对象的边界信息:===>", this.originalfabricObjects.length); console.log(
"计算当前所有对象的边界信息:===>",
this.originalfabricObjects.length
);
this.originalfabricObjects?.forEach((obj) => { this.originalfabricObjects?.forEach((obj) => {
const { object } = findObjectById(this.canvas, obj.id) || {}; const { object } = findObjectById(this.canvas, obj.id) || {};
if (object) { if (object) {
const rect = object.getBoundingRect({ absolute: true, includeStroke: false }); const rect = object.getBoundingRect({
absolute: true,
includeStroke: false,
});
minLeft = Math.min(minLeft, rect.left); minLeft = Math.min(minLeft, rect.left);
minTop = Math.min(minTop, rect.top); minTop = Math.min(minTop, rect.top);
maxRight = Math.max(maxRight, rect.left + rect.width); maxRight = Math.max(maxRight, rect.left + rect.width);

View File

@@ -224,7 +224,7 @@ export class AddObjectToLayerCommand extends Command {
isLocked: layer.locked || false, // 是否锁定 isLocked: layer.locked || false, // 是否锁定
isVisible: layer.visible !== false, // 是否可见 isVisible: layer.visible !== false, // 是否可见
eraser: layer.eraser || false, // 是否为橡皮擦 eraser: layer.eraser || false, // 是否为橡皮擦
type: this.fabricObject.type || "object", // 对象类型 // type: this.fabricObject.type || "object", // 对象类型
selectable: true, // 设置对象可选择 selectable: true, // 设置对象可选择
evented: true, // 设置对象可事件 evented: true, // 设置对象可事件
}); });

View File

@@ -1,6 +1,11 @@
import { Command } from "./Command"; import { Command } from "./Command";
import { fabric } from "fabric-with-all"; import { fabric } from "fabric-with-all";
import { LayerType, OperationType, createLayer, findLayerRecursively } from "../utils/layerHelper"; import {
LayerType,
OperationType,
createLayer,
findLayerRecursively,
} from "../utils/layerHelper";
import { import {
generateId, generateId,
optimizeCanvasRendering, optimizeCanvasRendering,
@@ -34,7 +39,10 @@ export class RasterizeLayerCommand extends Command {
this.layerManager = options.layerManager; 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.layer = layer;
this.parentLayer = parent; this.parentLayer = parent;
this.isGroupLayer = this.layer?.children && this.layer.children.length > 0; this.isGroupLayer = this.layer?.children && this.layer.children.length > 0;
@@ -94,7 +102,9 @@ export class RasterizeLayerCommand extends Command {
console.log(`✅ 图层 ${this.layer.name} 组合完成`); console.log(`✅ 图层 ${this.layer.name} 组合完成`);
this.canvas?.thumbnailManager?.generateLayerThumbnail?.(this.rasterizedLayerId); this.canvas?.thumbnailManager?.generateLayerThumbnail?.(
this.rasterizedLayerId
);
return this.rasterizedLayerId; return this.rasterizedLayerId;
} catch (error) { } catch (error) {
console.error("组合图层失败:", error); console.error("组合图层失败:", error);
@@ -330,7 +340,7 @@ export class RasterizeLayerCommand extends Command {
// 更新图像对象的图层关联 // 更新图像对象的图层关联
rasterizedImage.set({ rasterizedImage.set({
id: this.resterizedId, id: this.resterizedId,
type: "image", // type: "image",
layerId: this.rasterizedLayerId, layerId: this.rasterizedLayerId,
layerName: this.rasterizedLayer.name, layerName: this.rasterizedLayer.name,
}); });
@@ -352,7 +362,10 @@ export class RasterizeLayerCommand extends Command {
*/ */
_replaceLayerInStructure() { _replaceLayerInStructure() {
// 1.当前如果是子图层,则插入到子图层的位置 // 1.当前如果是子图层,则插入到子图层的位置
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId); const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
let insertIndex = 0; let insertIndex = 0;
// 说明是子图层 // 说明是子图层
@@ -373,7 +386,11 @@ export class RasterizeLayerCommand extends Command {
if (insertIndex !== -1) { if (insertIndex !== -1) {
if (parent) { if (parent) {
const pIndex = this.layers.value.findIndex((l) => l.id === parent.id); 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 { } else {
this.layers.value.splice(insertIndex, 1, this.rasterizedLayer); this.layers.value.splice(insertIndex, 1, this.rasterizedLayer);
} }
@@ -391,9 +408,14 @@ export class RasterizeLayerCommand extends Command {
async _getMaskObject() { async _getMaskObject() {
// 如果图层有clippingMask获取对应的fabric对象 // 如果图层有clippingMask获取对应的fabric对象
if (this.layer?.clippingMask) { 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) { if (maskObject) {
console.log(`📎 找到遮罩对象: ${maskObject.id}, 类型: ${maskObject.type}`); console.log(
`📎 找到遮罩对象: ${maskObject.id}, 类型: ${maskObject.type}`
);
return maskObject; return maskObject;
} }
return await restoreFabricObject(this.layer.clippingMask); return await restoreFabricObject(this.layer.clippingMask);
@@ -439,7 +461,10 @@ export class ExportLayerToImageCommand extends Command {
this.layerManager = options.layerManager; 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.layer = layer;
this.parentLayer = parent; this.parentLayer = parent;
this.isGroupLayer = this.layer?.children && this.layer.children.length > 0; this.isGroupLayer = this.layer?.children && this.layer.children.length > 0;
@@ -475,7 +500,10 @@ export class ExportLayerToImageCommand extends Command {
let clippingMaskFabricObject = null; let clippingMaskFabricObject = null;
if (this.layer?.clippingMask) { if (this.layer?.clippingMask) {
// 重新创建遮罩对象 // 重新创建遮罩对象
clippingMaskFabricObject = await restoreFabricObject(this.layer?.clippingMask, this.canvas); clippingMaskFabricObject = await restoreFabricObject(
this.layer?.clippingMask,
this.canvas
);
clippingMaskFabricObject.clipPath = null; clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({ clippingMaskFabricObject.set({

View File

@@ -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; targetLayerId.value = layerId;
try { 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; 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 const status = props.liquifyManager.getStatus
? props.liquifyManager.getStatus() ? props.liquifyManager.getStatus()
: props.liquifyManager.enhancedManager : props.liquifyManager.enhancedManager
? props.liquifyManager.enhancedManager.getStatus() ? props.liquifyManager.enhancedManager.getStatus()
: {}; : {};
webglAvailable.value = status.isWebGLAvailable || false; webglAvailable.value = status.isWebGLAvailable || false;
@@ -545,8 +557,8 @@ function showPanel(event) {
const status = props.liquifyManager.getStatus const status = props.liquifyManager.getStatus
? props.liquifyManager.getStatus() ? props.liquifyManager.getStatus()
: props.liquifyManager.enhancedManager : props.liquifyManager.enhancedManager
? props.liquifyManager.enhancedManager.getStatus() ? props.liquifyManager.enhancedManager.getStatus()
: {}; : {};
webglAvailable.value = status.isWebGLAvailable || false; webglAvailable.value = status.isWebGLAvailable || false;
@@ -605,7 +617,11 @@ function setTargetObject(obj) {
// 确保对象有唯一ID // 确保对象有唯一ID
if (!obj.id && !obj.objectId && !obj.uid) { 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; targetObject.value = obj;
@@ -673,7 +689,9 @@ function updateParam(paramName, value) {
`set${paramName.charAt(0).toUpperCase() + paramName.slice(1)}` `set${paramName.charAt(0).toUpperCase() + paramName.slice(1)}`
] === "function" ] === "function"
) { ) {
props.liquifyManager[`set${paramName.charAt(0).toUpperCase() + paramName.slice(1)}`](value); props.liquifyManager[
`set${paramName.charAt(0).toUpperCase() + paramName.slice(1)}`
](value);
} else { } else {
console.warn(`❌ 液化管理器不支持设置参数: ${paramName}`); console.warn(`❌ 液化管理器不支持设置参数: ${paramName}`);
} }
@@ -756,9 +774,19 @@ async function getCurrentImageData(targetObject) {
tempCtx.drawImage(element, 0, 0, tempCanvas.width, tempCanvas.height); tempCtx.drawImage(element, 0, 0, tempCanvas.width, tempCanvas.height);
// 获取ImageData // 获取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; return imageData;
} catch (error) { } catch (error) {
console.warn("获取当前图像数据失败:", error); console.warn("获取当前图像数据失败:", error);
@@ -837,7 +865,9 @@ async function handleMouseDown(event) {
const imageCoords = _convertFabricCoordsToImageCoords(pointer.x, pointer.y); const imageCoords = _convertFabricCoordsToImageCoords(pointer.x, pointer.y);
if (imageCoords) { if (imageCoords) {
props.liquifyManager.startLiquifyOperation(imageCoords.x, imageCoords.y); 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) { if (realtimeUpdater.value) {
try { try {
const currentImageElement = realtimeUpdater.value.getTargetObject()?._element; const currentImageElement =
realtimeUpdater.value.getTargetObject()?._element;
if (currentImageElement) { if (currentImageElement) {
// 从当前图像元素创建ImageData // 从当前图像元素创建ImageData
const tempCanvas = document.createElement("canvas"); const tempCanvas = document.createElement("canvas");
tempCanvas.width = initialImageData.value.width; tempCanvas.width = initialImageData.value.width;
tempCanvas.height = initialImageData.value.height; tempCanvas.height = initialImageData.value.height;
const tempCtx = tempCanvas.getContext("2d"); const tempCtx = tempCanvas.getContext("2d");
tempCtx.drawImage(currentImageElement, 0, 0, tempCanvas.width, tempCanvas.height); tempCtx.drawImage(
finalImageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height); currentImageElement,
0,
0,
tempCanvas.width,
tempCanvas.height
);
finalImageData = tempCtx.getImageData(
0,
0,
tempCanvas.width,
tempCanvas.height
);
console.log( console.log(
"✅ 从实时更新器获取最终图像数据成功,尺寸:", "✅ 从实时更新器获取最终图像数据成功,尺寸:",
@@ -962,8 +1004,19 @@ async function handleMouseUp() {
tempCanvas.width = initialImageData.value.width; tempCanvas.width = initialImageData.value.width;
tempCanvas.height = initialImageData.value.height; tempCanvas.height = initialImageData.value.height;
const tempCtx = tempCanvas.getContext("2d"); const tempCtx = tempCanvas.getContext("2d");
tempCtx.drawImage(currentTarget._element, 0, 0, tempCanvas.width, tempCanvas.height); tempCtx.drawImage(
finalImageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height); currentTarget._element,
0,
0,
tempCanvas.width,
tempCanvas.height
);
finalImageData = tempCtx.getImageData(
0,
0,
tempCanvas.width,
tempCanvas.height
);
console.log( console.log(
"✅ 从目标对象获取最终图像数据成功,尺寸:", "✅ 从目标对象获取最终图像数据成功,尺寸:",
@@ -1048,7 +1101,14 @@ async function applyLiquifyAtPoint(x, y) {
return; return;
} }
console.log("原始坐标:", x, y, "转换后图像坐标:", imageCoords.x, imageCoords.y); console.log(
"原始坐标:",
x,
y,
"转换后图像坐标:",
imageCoords.x,
imageCoords.y
);
// 获取当前参数 // 获取当前参数
const params = { const params = {
@@ -1113,7 +1173,8 @@ async function applyLiquifyAtPoint(x, y) {
targetObject.value = updatedObject; targetObject.value = updatedObject;
// 如果对象有新的ID也要更新ID // 如果对象有新的ID也要更新ID
if (updatedObject.id || updatedObject.objectId || updatedObject.uid) { 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) { if (avgOperationTime.value === 0) {
avgOperationTime.value = operationTime; avgOperationTime.value = operationTime;
} else { } 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) { if (layer.type === "background" || layer.isBackground) {
layer.fabricObject = img; layer.fabricObject = img;
} else if (layer.fabricObjects) { } 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) { if (objIndex !== -1) {
layer.fabricObjects[objIndex] = img; layer.fabricObjects[objIndex] = img;
} }
@@ -1304,7 +1368,10 @@ function _convertFabricCoordsToImageCoords(fabricX, fabricY) {
const matrix = fabric.util.invertTransform(transform); 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; 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( console.warn(
`坐标超出图像范围: (${imageX}, ${imageY}), 图像尺寸: ${imageWidth}x${imageHeight}` `坐标超出图像范围: (${imageX}, ${imageY}), 图像尺寸: ${imageWidth}x${imageHeight}`
); );
@@ -1539,9 +1611,7 @@ function stopPressTimer() {
.fade-enter-active, .fade-enter-active,
.fade-leave-active { .fade-leave-active {
transition: transition: opacity 0.3s, transform 0.3s;
opacity 0.3s,
transform 0.3s;
} }
.fade-enter-from, .fade-enter-from,
.fade-leave-to { .fade-leave-to {

View File

@@ -218,6 +218,7 @@ onMounted(async () => {
isFixedErasable: props.isFixedErasable, isFixedErasable: props.isFixedErasable,
}); });
canvasManager.canvas.activeLayerId = activeLayerId; canvasManager.canvas.activeLayerId = activeLayerId;
canvasManager.activeLayerId = activeLayerId;
canvasManager.canvas.activeElementId = activeElementId; canvasManager.canvas.activeElementId = activeElementId;
// 创建命令管理器 // 创建命令管理器
@@ -1207,8 +1208,8 @@ defineExpose({
bottom: 1rem; bottom: 1rem;
right: 1rem; right: 1rem;
background: rgba(255, 255, 255, 0.7); background: rgba(255, 255, 255, 0.7);
padding: .5rem 1rem; padding: 0.5rem 1rem;
border-radius: .4rem; border-radius: 0.4rem;
font-size: 1.4rem; font-size: 1.4rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
color: #666; color: #666;
@@ -1220,8 +1221,8 @@ defineExpose({
top: 1rem; top: 1rem;
left: 1rem; left: 1rem;
background: rgba(255, 255, 255, 0.8); background: rgba(255, 255, 255, 0.8);
padding: .5rem 1rem; padding: 0.5rem 1rem;
border-radius: .4rem; border-radius: 0.4rem;
font-size: 1.2rem; font-size: 1.2rem;
color: #666; color: #666;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
@@ -1230,11 +1231,11 @@ defineExpose({
.reset-zoom { .reset-zoom {
margin-left: 1rem; margin-left: 1rem;
cursor: pointer; cursor: pointer;
padding: .2rem .5rem; padding: 0.2rem 0.5rem;
font-size: 1.2rem; font-size: 1.2rem;
border: 1px solid #ddd; border: 1px solid #ddd;
background: #f8f8f8; background: #f8f8f8;
border-radius: .3rem; border-radius: 0.3rem;
} }
.footer-actions { .footer-actions {
@@ -1247,7 +1248,7 @@ defineExpose({
.share-btn, .share-btn,
.export-btn { .export-btn {
padding: .8rem 2rem; padding: 0.8rem 2rem;
border: none; border: none;
border-radius: 2rem; border-radius: 2rem;
background-color: #000; background-color: #000;
@@ -1310,7 +1311,7 @@ button:hover {
max-height: 90vh; max-height: 90vh;
overflow-y: auto; overflow-y: auto;
background-color: #fff; background-color: #fff;
border-radius: .8rem; border-radius: 0.8rem;
padding: 2rem; padding: 2rem;
} }
@@ -1337,11 +1338,11 @@ button:hover {
right: 1rem; right: 1rem;
background: rgba(255, 255, 255, 0.9); background: rgba(255, 255, 255, 0.9);
padding: 1rem; padding: 1rem;
border-radius: .4rem; border-radius: 0.4rem;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2); box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: .8rem; gap: 0.8rem;
z-index: 5; z-index: 5;
} }
@@ -1353,7 +1354,7 @@ button:hover {
background: #fff; background: #fff;
width: 35rem; width: 35rem;
max-height: 85vh; 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); /* 添加模糊效果 */ backdrop-filter: blur(2px); /* 添加模糊效果 */
-webkit-backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);
background-color: rgba(255, 255, 255, 0.95); /* 改为白色背景 */ background-color: rgba(255, 255, 255, 0.95); /* 改为白色背景 */
@@ -1363,8 +1364,8 @@ button:hover {
&::before { &::before {
content: ""; content: "";
position: absolute; position: absolute;
top: -.9rem; top: -0.9rem;
right: .6rem; right: 0.6rem;
width: 0; width: 0;
height: 0; height: 0;
border-left: 1rem solid transparent; border-left: 1rem solid transparent;
@@ -1388,7 +1389,7 @@ button:hover {
.layer-item, .layer-item,
.element-item { .element-item {
padding: 1.2rem .8rem; padding: 1.2rem 0.8rem;
} }
.element-action-btn, .element-action-btn,

View File

@@ -903,7 +903,10 @@ export class LayerManager {
* @returns {Object|null} 图层对象或null * @returns {Object|null} 图层对象或null
*/ */
getLayerById(layerId) { getLayerById(layerId) {
const { layer } = findLayerRecursively(this.layers.value, layerId); const { layer } = findLayerRecursively(
this.layers?.value ?? this.layers,
layerId
);
return layer; return layer;
} }

View File

@@ -3,7 +3,7 @@ import { BrushManager } from "./brushes/brushManager";
import { BrushIndicator } from "./BrushIndicator"; import { BrushIndicator } from "./BrushIndicator";
import { ToolCommand } from "../commands/ToolCommands"; import { ToolCommand } from "../commands/ToolCommands";
import { CreateTextCommand } from "../commands/TextCommands"; import { CreateTextCommand } from "../commands/TextCommands";
import { OperationType } from "../utils/layerHelper"; import { findLayerRecursively, OperationType } from "../utils/layerHelper";
import CanvasConfig from "../config/canvasConfig"; import CanvasConfig from "../config/canvasConfig";
import { fabric } from "fabric-with-all"; import { fabric } from "fabric-with-all";
import { InitLiquifyToolCommand } from "../commands/LiquifyCommands"; import { InitLiquifyToolCommand } from "../commands/LiquifyCommands";
@@ -349,6 +349,11 @@ export class ToolManager {
// 保存先前的工具 // 保存先前的工具
this.previousTool = this.activeTool.value; this.previousTool = this.activeTool.value;
// 取消画布的选中状态
this.canvas?.discardActiveObject();
this.canvasManager?.layerManager?.updateLayersObjectsInteractivity?.();
this.canvas?.renderAll();
// 如果切换到非画笔工具,禁用笔刷指示器 // 如果切换到非画笔工具,禁用笔刷指示器
if (!this._shouldShowBrushIndicator(toolId)) { if (!this._shouldShowBrushIndicator(toolId)) {
this._disableBrushIndicator(); this._disableBrushIndicator();
@@ -665,7 +670,11 @@ export class ToolManager {
panelDetail.layerStatus = checkResult; 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) { if (!checkResult.isEmpty) {
@@ -844,7 +853,12 @@ export class ToolManager {
*/ */
async _startLiquify(layerId) { 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) { if (!layer) {
Modal.error({ Modal.error({
title: "图层错误", title: "图层错误",
@@ -1122,7 +1136,11 @@ export class ToolManager {
target.type === "textbox") 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) { if (layer) {
// 显示文本编辑面板 // 显示文本编辑面板
this.showTextEditor(target, layer); this.showTextEditor(target, layer);

View File

@@ -4,7 +4,7 @@
*/ */
import { LiquifyWebGLManager } from "./LiquifyWebGLManager"; import { LiquifyWebGLManager } from "./LiquifyWebGLManager";
import { LiquifyCPUManager } from "./LiquifyCPUManager"; import { LiquifyCPUManager } from "./LiquifyCPUManager";
import { findInChildLayers, LayerType } from "../../utils/layerHelper"; import { findLayerRecursively, LayerType } from "../../utils/layerHelper";
export class EnhancedLiquifyManager { export class EnhancedLiquifyManager {
/** /**
@@ -140,7 +140,10 @@ export class EnhancedLiquifyManager {
// 处理传入的是图层ID的情况 // 处理传入的是图层ID的情况
if (typeof target === "string") { if (typeof target === "string") {
targetLayerId = target; targetLayerId = target;
const layer = this.layerManager.getLayerById(targetLayerId); const { layer } = findLayerRecursively(
this.layerManager.layers?.value ?? this.layerManager.layers,
targetLayerId
);
// 检查图层是否存在和是否有对象 // 检查图层是否存在和是否有对象
let hasObjects = false; let hasObjects = false;
@@ -160,7 +163,7 @@ export class EnhancedLiquifyManager {
} else if (typeof target === "object") { } else if (typeof target === "object") {
// 传入的是对象 // 传入的是对象
targetObject = target; targetObject = target;
const { layer } = findInChildLayers( const { layer } = findLayerRecursively(
this.layerManager.layers?.value ?? this.layerManager.layers, this.layerManager.layers?.value ?? this.layerManager.layers,
targetObject.layerId targetObject.layerId
); );
@@ -216,7 +219,9 @@ export class EnhancedLiquifyManager {
// 计算图像大小 // 计算图像大小
const pixelCount = imageData.width * imageData.height; const pixelCount = imageData.width * imageData.height;
console.log(`液化选择渲染器: 图像大小=${pixelCount}像素, WebGL可用=${this.isWebGLAvailable}`); console.log(
`液化选择渲染器: 图像大小=${pixelCount}像素, WebGL可用=${this.isWebGLAvailable}`
);
// 默认使用CPU渲染器 // 默认使用CPU渲染器
this.activeRenderer = this.cpuRenderer; this.activeRenderer = this.cpuRenderer;
@@ -247,7 +252,11 @@ export class EnhancedLiquifyManager {
this.activeRenderer = this.webglRenderer; this.activeRenderer = this.webglRenderer;
this.renderMode = "webgl"; this.renderMode = "webgl";
} else { } else {
console.log(`液化功能: 使用CPU渲染模式${!this.isWebGLAvailable ? " (WebGL不可用)" : ""}`); console.log(
`液化功能: 使用CPU渲染模式${
!this.isWebGLAvailable ? " (WebGL不可用)" : ""
}`
);
} }
} }
@@ -312,11 +321,16 @@ export class EnhancedLiquifyManager {
this.params[param] = value; 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}`); console.log(`EnhancedLiquifyManager 设置参数: ${param}=${value}`);
this.activeRenderer.setParam(param, value); this.activeRenderer.setParam(param, value);
} else { } else {
console.warn(`EnhancedLiquifyManager: 无法设置参数 ${param},渲染器未就绪`); console.warn(
`EnhancedLiquifyManager: 无法设置参数 ${param},渲染器未就绪`
);
} }
return true; return true;
@@ -371,17 +385,25 @@ export class EnhancedLiquifyManager {
* @param {Number} y 初始Y坐标 * @param {Number} y 初始Y坐标
*/ */
startLiquifyOperation(x, 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); this.activeRenderer.startDeformation(x, y);
} }
console.log(`开始液化操作,渲染模式=${this.renderMode}, 初始点: (${x}, ${y})`); console.log(
`开始液化操作,渲染模式=${this.renderMode}, 初始点: (${x}, ${y})`
);
} }
/** /**
* 结束液化操作 * 结束液化操作
*/ */
endLiquifyOperation() { endLiquifyOperation() {
if (this.activeRenderer && typeof this.activeRenderer.endDeformation === "function") { if (
this.activeRenderer &&
typeof this.activeRenderer.endDeformation === "function"
) {
this.activeRenderer.endDeformation(); this.activeRenderer.endDeformation();
} }
console.log(`结束液化操作,渲染模式=${this.renderMode}`); console.log(`结束液化操作,渲染模式=${this.renderMode}`);
@@ -424,7 +446,9 @@ export class EnhancedLiquifyManager {
// 坐标边界检查 // 坐标边界检查
if (x < 0 || x >= imageWidth || y < 0 || y >= imageHeight) { 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; return null;
} }
@@ -489,7 +513,9 @@ export class EnhancedLiquifyManager {
console.log( console.log(
`液化性能数据: 模式=${this.renderMode}, 平均耗时=${avgTime.toFixed( `液化性能数据: 模式=${this.renderMode}, 平均耗时=${avgTime.toFixed(
2 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) { if (!layer) {
return { return {
valid: false, valid: false,
@@ -581,7 +611,8 @@ export class EnhancedLiquifyManager {
const singleObject = objectsToCheck.length === 1; const singleObject = objectsToCheck.length === 1;
const isImage = const isImage =
singleObject && singleObject &&
(objectsToCheck[0].type === "image" || objectsToCheck[0].type === "rasterized-layer"); (objectsToCheck[0].type === "image" ||
objectsToCheck[0].type === "rasterized-layer");
// 检查是否为组 // 检查是否为组
const isGroup = const isGroup =
@@ -631,14 +662,19 @@ export class EnhancedLiquifyManager {
tempCanvas.height = fabricObject.height; tempCanvas.height = fabricObject.height;
const tempCtx = tempCanvas.getContext("2d"); const tempCtx = tempCanvas.getContext("2d");
console.log(`创建临时Canvas尺寸: ${tempCanvas.width}x${tempCanvas.height}`); console.log(
`创建临时Canvas尺寸: ${tempCanvas.width}x${tempCanvas.height}`
);
// 处理不同的图像源 // 处理不同的图像源
if (fabricObject._element) { if (fabricObject._element) {
console.log("使用 _element 绘制图像"); console.log("使用 _element 绘制图像");
// 检查_element是否有效 // 检查_element是否有效
if (!fabricObject._element.complete && fabricObject._element.tagName === "IMG") { if (
!fabricObject._element.complete &&
fabricObject._element.tagName === "IMG"
) {
console.log("图像未加载完成,等待加载..."); console.log("图像未加载完成,等待加载...");
fabricObject._element.onload = () => { fabricObject._element.onload = () => {
try { try {
@@ -649,7 +685,12 @@ export class EnhancedLiquifyManager {
fabricObject.width, fabricObject.width,
fabricObject.height fabricObject.height
); );
const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height); const imageData = tempCtx.getImageData(
0,
0,
tempCanvas.width,
tempCanvas.height
);
console.log("✅ 图像加载完成后获取数据成功"); console.log("✅ 图像加载完成后获取数据成功");
resolve(imageData); resolve(imageData);
} catch (error) { } catch (error) {
@@ -664,8 +705,17 @@ export class EnhancedLiquifyManager {
} }
// 直接绘制已加载的图像 // 直接绘制已加载的图像
tempCtx.drawImage(fabricObject._element, 0, 0, fabricObject.width, fabricObject.height); tempCtx.drawImage(
} else if (fabricObject.getSrc && typeof fabricObject.getSrc === "function") { fabricObject._element,
0,
0,
fabricObject.width,
fabricObject.height
);
} else if (
fabricObject.getSrc &&
typeof fabricObject.getSrc === "function"
) {
console.log("使用 getSrc() 方法获取图像源"); console.log("使用 getSrc() 方法获取图像源");
// 通过URL创建图像 // 通过URL创建图像
@@ -674,11 +724,24 @@ export class EnhancedLiquifyManager {
img.onload = () => { img.onload = () => {
try { 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获取图像数据成功"); console.log("✅ 通过URL获取图像数据成功");
resolve(imageData); resolve(imageData);
@@ -706,9 +769,20 @@ export class EnhancedLiquifyManager {
img.onload = () => { img.onload = () => {
try { 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属性获取图像数据成功"); console.log("✅ 通过src属性获取图像数据成功");
resolve(imageData); resolve(imageData);
@@ -728,13 +802,20 @@ export class EnhancedLiquifyManager {
return; return;
} else { } else {
console.error("无法找到有效的图像源"); console.error("无法找到有效的图像源");
reject(new Error("图像对象缺少有效的图像源_element, getSrc, 或 src")); reject(
new Error("图像对象缺少有效的图像源_element, getSrc, 或 src")
);
return; return;
} }
// 如果走到这里说明使用了_element直接绘制 // 如果走到这里说明使用了_element直接绘制
try { try {
const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height); const imageData = tempCtx.getImageData(
0,
0,
tempCanvas.width,
tempCanvas.height
);
console.log( console.log(
`✅ 获取图像数据成功: 对象尺寸=${fabricObject.width}x${fabricObject.height}, ` + `✅ 获取图像数据成功: 对象尺寸=${fabricObject.width}x${fabricObject.height}, ` +

View File

@@ -95,7 +95,8 @@ export const BlendMode = {
export function isGroupLayer(layer) { export function isGroupLayer(layer) {
if (!layer) return false; if (!layer) return false;
return ( 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 其他选项 * @param {Object} options 其他选项
* @returns {Object} 创建的图层对象 * @returns {Object} 创建的图层对象
*/ */
export function createLayerFromFabricObject(fabricObject, layerType = "bitmap", options = {}) { export function createLayerFromFabricObject(
fabricObject,
layerType = "bitmap",
options = {}
) {
if (!fabricObject) return null; if (!fabricObject) return null;
// 确定图层类型 // 确定图层类型
@@ -132,7 +137,9 @@ export function createLayerFromFabricObject(fabricObject, layerType = "bitmap",
type: type, type: type,
name: name:
options.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, parentId: options.parentId || null,
}); });
@@ -159,7 +166,9 @@ export function createLayerFromFabricObject(fabricObject, layerType = "bitmap",
*/ */
export function createLayer(options = {}) { export function createLayer(options = {}) {
const id = 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 { return {
id: id, id: id,
// 图层基本属性 // 图层基本属性
@@ -199,7 +208,8 @@ export function createLayer(options = {}) {
* @returns {Object} 背景图层对象 * @returns {Object} 背景图层对象
*/ */
export function createBackgroundLayer(options = {}) { 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 { return {
id: id, id: id,
// 图层基本属性 // 图层基本属性
@@ -390,7 +400,9 @@ export function createSmartObjectLayer(options = {}) {
* @returns {Object} 固定图层对象 * @returns {Object} 固定图层对象
*/ */
export function createFixedLayer(options = {}) { 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 { return {
id: id, id: id,
// 图层基本属性 // 图层基本属性
@@ -433,7 +445,9 @@ export function cloneLayer(layer) {
// 复制 fabric 对象 (如果存在) // 复制 fabric 对象 (如果存在)
if (Array.isArray(layer.fabricObjects)) { if (Array.isArray(layer.fabricObjects)) {
clonedLayer.fabricObjects = layer.fabricObjects.map((obj) => { 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));
}); });
} }