feat(CanvasManager): enhance image layer management and event handling

This commit is contained in:
bighuixiang
2025-06-26 00:37:07 +08:00
parent afa3b69f71
commit 2fcba962d1
16 changed files with 901 additions and 448 deletions

View File

@@ -35,13 +35,19 @@ import { fabric } from "fabric-with-all";
import {
uploadImageAndCreateLayer,
loadImageUrlToLayer,
loadImage,
} from "./utils/imageHelper.js";
import { next } from "lodash-es";
// import MinimapPanel from "./components/MinimapPanel.vue";
const KeyboardShortcutHelp = defineAsyncComponent(() =>
import("./components/KeyboardShortcutHelp.vue")
);
const emit = defineEmits(["trigger-red-green-mouseup", "changeCanvas"]);
const emit = defineEmits([
"trigger-red-green-mouseup", // 红绿图模式鼠标抬起事件
"changeCanvas", // 画布变更事件
"canvasInit", // 画布初始化事件
]);
const props = defineProps({
canvasJSON: {
@@ -151,6 +157,24 @@ function handleToolSelect(tool) {
}); // 命令模式 可撤回操作
}
// 触发组件初始化事件
function handleCanvasInit(isLoadJson = false) {
emit("canvasInit", {
isLoadJson: isLoadJson ?? !!props.canvasJSON, // 是否加载了JSON数据
isRedGreenMode: isRedGreenMode.value,
layers,
activeLayerId,
canvasManager,
layerManager,
commandManager,
toolManager,
keyboardManager,
liquifyManager,
selectionManager,
redGreenModeManager,
});
}
function toggleMinimap(enabled) {
// minimapEnabled.value = enabled;
// if (minimapManager.value) {
@@ -177,6 +201,7 @@ onMounted(async () => {
canvasHeight,
canvasColor,
enabledRedGreenMode: props.enabledRedGreenMode,
isFixedErasable: props.isFixedErasable,
});
canvasManager.canvas.activeLayerId = activeLayerId;
canvasManager.canvas.activeElementId = activeElementId;
@@ -205,24 +230,6 @@ onMounted(async () => {
// 设置缩略图管理器需要访问的图层数据
// canvasManager.layers = layers;
if (props.canvasJSON) {
// 如果传入了初始JSON数据加载到画布上
if (typeof props.canvasJSON === "string") {
try {
canvasManager.loadJSON(props.canvasJSON);
} catch (error) {
console.error("加载画布JSON失败:", error);
// 初始化图层 - 确保创建背景层
layerManager.initializeLayers();
}
} else if (typeof props.canvasJSON === "object") {
canvasManager.loadJSON(JSON.stringify(props.canvasJSON));
}
} else {
// 初始化图层 - 确保创建背景层
layerManager.initializeLayers();
}
// 创建工具管理器实例
toolManager = new ToolManager({
canvas: canvasManager.canvas, // fabric.js 画布实例
@@ -253,6 +260,9 @@ onMounted(async () => {
// 绑定快捷键事件
keyboardManager.init();
// 绑定画布操作事件
canvasManager.setupCanvasEvents(activeElementId, layerManager);
canvasManager.setupCanvasInitEvent(handleCanvasInit); // 绑定画布初始化事件
provide("canvasManager", canvasManager); // 提供给子组件使用
provide("layerManager", layerManager); // 提供给子组件使用
@@ -262,27 +272,12 @@ onMounted(async () => {
provide("activeTool", activeTool); // 提供给子组件使用
provide("liquifyManager", () => liquifyManager); // 提供液化管理器
provide("layers", layers); // 提供图层数据
// 绑定画布操作事件
canvasManager.setupCanvasEvents(activeElementId, layerManager);
canvasManagerLoaded.value = true;
// 初始化网格设置
// toggleGridVisibility(gridEnabled.value);
// 初始化小地图
// minimapManager.value = new MinimapManager(canvasManager.canvas);
setTimeout(() => {
// 初始状态下生成所有预览图
canvasManager.updateAllThumbnails();
}, 300);
// 使用window的resize事件代替ResizeObserver
// 只有当窗口大小变化时才更新画布尺寸
window.addEventListener("resize", handleWindowResize);
// 初始化液化管理器
liquifyManager = new LiquifyManager({
canvas: canvasManager.canvas,
@@ -298,6 +293,24 @@ onMounted(async () => {
});
canvasManager.setSelectionManager(selectionManager);
if (props.canvasJSON) {
// 如果传入了初始JSON数据加载到画布上
if (typeof props.canvasJSON === "string") {
try {
await canvasManager.loadJSON(props.canvasJSON);
} catch (error) {
console.error("加载画布JSON失败:", error);
// 初始化图层 - 确保创建背景层
await layerManager.initializeLayers();
}
} else if (typeof props.canvasJSON === "object") {
await canvasManager.loadJSON(JSON.stringify(props.canvasJSON));
}
} else {
// 初始化图层 - 确保创建背景层
await layerManager.initializeLayers();
}
if (
props.enabledRedGreenMode &&
props.clothingImageUrl &&
@@ -340,18 +353,14 @@ onMounted(async () => {
// 初始设置
handleWindowResize(); // 设置画布大小
} else if (!isRedGreenMode.value && props.clothingImageUrl) {
nextTick(() => {
setTimeout(() => {
try {
canvasManager?.changeFixedImage?.(props.clothingImageUrl, {
undoable: false, // 不可撤销操作
...(props?.clothingImageOpts || {}),
});
} catch (error) {
console.error("更换底图失败:", error);
}
}, 92); // 延迟 确保更新底图完成
});
try {
await canvasManager?.changeFixedImage?.(props.clothingImageUrl, {
undoable: false, // 不可撤销操作
...(props?.clothingImageOpts || {}),
});
} catch (error) {
console.error("更换底图失败:", error);
}
canvasManager?.centerBackgroundLayer?.(
canvasManager.canvas.width,
@@ -369,6 +378,24 @@ onMounted(async () => {
// type: "isBackground",
// flag: !props.isBackgroundErasable, // 设置操作类型为可擦除
// });
canvasManagerLoaded.value = true;
// 触发组件初始化事件
nextTick(() => {
// 确保所有依赖都已加载完成
handleCanvasInit();
requestAnimationFrame(() => {
setTimeout(() => {
// 初始状态下生成所有预览图
canvasManager.updateAllThumbnails();
}, 300);
});
});
// 使用window的resize事件代替ResizeObserver
// 只有当窗口大小变化时才更新画布尺寸
window.addEventListener("resize", handleWindowResize);
});
watchEffect(() => {
@@ -707,18 +734,38 @@ defineExpose({
},
// (更换底图,不可撤销,不可操作)
changeFixedImage: (url, opts) => {
return canvasManager?.changeFixedImage?.(url, opts);
return canvasManager?.changeFixedImage?.(url, {
...(props?.clothingImageOpts || {}),
...opts,
});
},
//图片url或者base64
addImageToLayer: async (url) => {
addImageToLayer: async (
url,
{ layerId, undoable } = { layerId: null, undoable: true } // 可选参数 layerId 指定图层 将内容添加到指定图层 undoable 是否可撤销 false不可撤销 默认可撤销
) => {
if (!url) return Promise.reject(new Error("图片URL不能为空"));
return await loadImageUrlToLayer({
imageUrl: url,
layerManager,
canvas: canvasManager.canvas,
toolManager,
});
if (layerId) {
const fabricImage = await loadImage(url);
// 如果指定了图层ID确保图层存在
return await canvasManager?.addImageToLayer?.(url, {
targetLayerId: layerId,
fabricImage,
undoable, // 是否可撤销操作
});
}
// 未指定图层ID默认添加到新的图层
return await loadImageUrlToLayer(
{
imageUrl: url,
layerManager,
canvas: canvasManager.canvas,
toolManager,
},
{ undoable }
);
},
// 导出图片
exportImage: ({