Merge branch 'StableVersion' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite

This commit is contained in:
李志鹏
2026-04-14 16:24:42 +08:00
11 changed files with 3196 additions and 3174 deletions

View File

@@ -55,6 +55,7 @@ commandManager.setChangeCallback((info) => {
emit("undo-redo-status-changed", { emit("undo-redo-status-changed", {
canUndo: canUndo.value, canUndo: canUndo.value,
canRedo: canRedo.value, canRedo: canRedo.value,
type: info.type,
commandManager, commandManager,
}); });
}); });

View File

@@ -907,7 +907,8 @@
} }
emit("changeCanvas", commandData) emit("changeCanvas", commandData)
canvasManager.changeCanvas() canvasManager.changeCanvas()
if ((command.canUndo || command.canRedo) && props.enabledRedGreenMode) { const type = command.type
if (props.enabledRedGreenMode && (type === "undo" || type === "redo")) {
setTimeout(async () => { setTimeout(async () => {
try { try {
const imageData = await canvasManager.exportImage({ const imageData = await canvasManager.exportImage({
@@ -1057,7 +1058,10 @@
} = {}) => { } = {}) => {
loading.value = true loading.value = true
canvasManager?.canvas?.discardActiveObject() canvasManager?.canvas?.discardActiveObject()
if(isFrontBackUpdata)await canvasManager?.changeCanvas() if (isFrontBackUpdata) {
await canvasManager?.setSpecialCliptInfo(true, true)
canvasManager.canvas.renderAll()
}
var base64 = await canvasManager.exportImage({ var base64 = await canvasManager.exportImage({
isContainBg, isContainBg,
isContainFixed, isContainFixed,

View File

@@ -47,7 +47,7 @@ import { getObjectAlphaToCanvas } from "../utils/objectHelper";
import { AddLayerCommand, RemoveLayerCommand, ToggleChildLayerVisibilityCommand } from "../commands/LayerCommands"; import { AddLayerCommand, RemoveLayerCommand, ToggleChildLayerVisibilityCommand } from "../commands/LayerCommands";
import { fa, id } from "element-plus/es/locales.mjs"; import { fa, id } from "element-plus/es/locales.mjs";
import i18n from "@/lang/index.ts"; import i18n from "@/lang/index.ts";
const {t} = i18n.global; const { t } = i18n.global;
export class CanvasManager { export class CanvasManager {
constructor(canvasElement, options) { constructor(canvasElement, options) {
@@ -70,7 +70,7 @@ export class CanvasManager {
this.handleCanvasInit = null; // 画布初始化回调函数 this.handleCanvasInit = null; // 画布初始化回调函数
this.partManager = options.partManager || null; this.partManager = options.partManager || null;
this.props = options.props || {}; this.props = options.props || {};
this.emit = options.emit || (() => {}); this.emit = options.emit || (() => { });
this.awaitCanvasRun = null; this.awaitCanvasRun = null;
this.canvasChangeing = false; this.canvasChangeing = false;
// 初始化画布 // 初始化画布
@@ -176,9 +176,9 @@ export class CanvasManager {
// 添加笔刷图像转换处理回调 // 添加笔刷图像转换处理回调
this.canvas.onBrushImageConverted = async (fabricImage) => { this.canvas.onBrushImageConverted = async (fabricImage) => {
const activeTool = this.toolManager?.activeTool?.value; const activeTool = this.toolManager?.activeTool?.value;
if(activeTool === OperationType.PART_BRUSH){ if (activeTool === OperationType.PART_BRUSH) {
this.partManager?.addDrawPartImage(fabricImage); this.partManager?.addDrawPartImage(fabricImage);
}else{ } else {
await this.addImageToLayer({ fabricImage, targetLayerId: null }); await this.addImageToLayer({ fabricImage, targetLayerId: null });
} }
// 返回false表示使用默认行为直接添加到画布 // 返回false表示使用默认行为直接添加到画布
@@ -203,7 +203,7 @@ export class CanvasManager {
// 可以在这里保存状态到命令管理器 // 可以在这里保存状态到命令管理器
const affectedObjects = e.targets || []; const affectedObjects = e.targets || [];
const activeTool = this.toolManager?.activeTool?.value; const activeTool = this.toolManager?.activeTool?.value;
if(activeTool === OperationType.PART_ERASER){ if (activeTool === OperationType.PART_ERASER) {
return this.partManager?.onErasingEnd(affectedObjects); return this.partManager?.onErasingEnd(affectedObjects);
} }
const command = this.eraserStateManager.endErasing(affectedObjects); const command = this.eraserStateManager.endErasing(affectedObjects);
@@ -461,7 +461,7 @@ export class CanvasManager {
// this.canvas.renderAll(); // this.canvas.renderAll();
} }
// 重置画布大小参照固定图层 // 重置画布大小参照固定图层
async resetCanvasSizeByFixedLayer(){ async resetCanvasSizeByFixedLayer() {
// 重置画布大小为固定图层的大小 // 重置画布大小为固定图层的大小
const fixedLayerObj = this.getFixedLayerObject(); const fixedLayerObj = this.getFixedLayerObject();
const backgroundObject = this.getBackgroundLayerObject(); const backgroundObject = this.getBackgroundLayerObject();
@@ -475,10 +475,10 @@ export class CanvasManager {
fixedLayerObj.height, fixedLayerObj.height,
fixedLayerObj.scaleY, fixedLayerObj.scaleY,
backgroundObject.width, backgroundObject.width,
backgroundObject.scaleX, backgroundObject.scaleX,
backgroundObject.height, backgroundObject.height,
backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer') backgroundObject.scaleY, 'CanvasManager resetCanvasSizeByFixedLayer')
if(Math.abs(fwidth/bwidth - fheight/bheight) < 0.1) return; if (Math.abs(fwidth / bwidth - fheight / bheight) < 0.1) return;
this.canvasWidth.value = fwidth this.canvasWidth.value = fwidth
this.canvasHeight.value = fheight this.canvasHeight.value = fheight
backgroundObject.set({ backgroundObject.set({
@@ -489,7 +489,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
width: this.canvasWidth.value, width: this.canvasWidth.value,
height: this.canvasHeight.value, height: this.canvasHeight.value,
}) })
} }
/** /**
* 重置视图变换,使元素回到原始位置 * 重置视图变换,使元素回到原始位置
* @private * @private
@@ -623,17 +623,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
this.updateMaskPosition(backgroundObject); this.updateMaskPosition(backgroundObject);
} }
// 更新颜色层信息 this.setSpecialCliptInfo(false, true)
// const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
// if(colorObject){
// await this.setObjecCliptInfo(colorObject);
// }
const groupLayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
if(groupLayer){
const groupRect = new fabric.Rect({});
await this.setObjecCliptInfo(groupRect);
groupLayer.clippingMask = groupRect.toObject();
}
// 重新渲染画布 // 重新渲染画布
this.canvas.renderAll(); this.canvas.renderAll();
@@ -868,7 +858,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
return layerObjectByLayerId; return layerObjectByLayerId;
} }
getObjectsByIdOrLayerId(ids){ getObjectsByIdOrLayerId(ids) {
const objects = this.canvas.getObjects().filter((obj) => { const objects = this.canvas.getObjects().filter((obj) => {
return ids.includes(obj.id) || ids.includes(obj.layerId); return ids.includes(obj.id) || ids.includes(obj.layerId);
}); });
@@ -1035,16 +1025,16 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
// 处理特殊图层的显示状态 // 处理特殊图层的显示状态
const ptlids = []; const ptlids = [];
if(!enhancedOptions.isPrintTrimsNoRepeat || !enhancedOptions.isPrintTrimsRepeat){ if (!enhancedOptions.isPrintTrimsNoRepeat || !enhancedOptions.isPrintTrimsRepeat) {
let layers = this.layers?.value?.find((layer) => layer.isPrintTrimsGroup)?.children || []; let layers = this.layers?.value?.find((layer) => layer.isPrintTrimsGroup)?.children || [];
for(let layer of layers){ for (let layer of layers) {
if(!layer.visible) continue; if (!layer.visible) continue;
let repeat = layer.fabricObjects?.[0]?.fill?.repeat || "no-repeat"; let repeat = layer.fabricObjects?.[0]?.fill?.repeat || "no-repeat";
if(typeof repeat !== "string") repeat = "no-repeat"; if (typeof repeat !== "string") repeat = "no-repeat";
if(repeat === "no-repeat"){ if (repeat === "no-repeat") {
if(enhancedOptions.isPrintTrimsNoRepeat) continue; if (enhancedOptions.isPrintTrimsNoRepeat) continue;
}else{ } else {
if(enhancedOptions.isPrintTrimsRepeat) continue; if (enhancedOptions.isPrintTrimsRepeat) continue;
} }
ptlids.push(layer.id); ptlids.push(layer.id);
const command = new ToggleChildLayerVisibilityCommand({ const command = new ToggleChildLayerVisibilityCommand({
@@ -1059,8 +1049,8 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
} }
const res = await this.exportManager.exportImage(enhancedOptions); const res = await this.exportManager.exportImage(enhancedOptions);
// 恢复特殊图层的显示状态 // 恢复特殊图层的显示状态
if(ptlids.length > 0){ if (ptlids.length > 0) {
for(let id of ptlids){ for (let id of ptlids) {
const command = new ToggleChildLayerVisibilityCommand({ const command = new ToggleChildLayerVisibilityCommand({
canvas: this.canvas, canvas: this.canvas,
layers: this.layers, layers: this.layers,
@@ -1086,7 +1076,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
// 导出颜色图层信息 // 导出颜色图层信息
const color = await this.exportColorLayer().catch(() => (null)); const color = await this.exportColorLayer().catch(() => (null));
// 导出印花和元素图层信息 // 导出印花和元素图层信息
const printTrimsData = await this.exportPrintTrimsLayers().catch(() => ({prints: null, trims: null})); const printTrimsData = await this.exportPrintTrimsLayers().catch(() => ({ prints: null, trims: null }));
const obj = { const obj = {
color, color,
@@ -1106,7 +1096,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
return Promise.reject("颜色图层不存在"); return Promise.reject("颜色图层不存在");
} }
const object = this.getLayerObjectById(SpecialLayerId.COLOR); const object = this.getLayerObjectById(SpecialLayerId.COLOR);
if(!object){ if (!object) {
console.warn("颜色图层不存在,请确保已添加颜色图层"); console.warn("颜色图层不存在,请确保已添加颜色图层");
return Promise.reject("颜色图层不存在"); return Promise.reject("颜色图层不存在");
} }
@@ -1137,7 +1127,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
}); });
canvas.clear(); canvas.clear();
const color = object.originColor; const color = object.originColor;
return {css, base64, color}; return { css, base64, color };
} }
/** /**
@@ -1145,11 +1135,11 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
*/ */
async exportPrintTrimsLayers() { async exportPrintTrimsLayers() {
const glayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP); const glayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
if(!glayer) return Promise.reject("印花和元素图层组不存在"); if (!glayer) return Promise.reject("印花和元素图层组不存在");
const ids = glayer.children.map((v) => v.id); const ids = glayer.children.map((v) => v.id);
const objects = this.getObjectsByIdOrLayerId(ids); const objects = this.getObjectsByIdOrLayerId(ids);
const fixedLayerObj = this.getFixedLayerObject(); const fixedLayerObj = this.getFixedLayerObject();
if(!fixedLayerObj) return Promise.reject("固定图层不存在"); if (!fixedLayerObj) return Promise.reject("固定图层不存在");
const flWidth = fixedLayerObj.width const flWidth = fixedLayerObj.width
const flHeight = fixedLayerObj.height const flHeight = fixedLayerObj.height
const flTop = fixedLayerObj.top const flTop = fixedLayerObj.top
@@ -1161,7 +1151,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
objects.forEach((v, i) => { objects.forEach((v, i) => {
const label = glayer.children.find((v_) => (v_.id === v.layerId || v_.id === v.id)); const label = glayer.children.find((v_) => (v_.id === v.layerId || v_.id === v.id));
const sourceData = label?.metadata?.sourceData; const sourceData = label?.metadata?.sourceData;
if(!sourceData) return; if (!sourceData) return;
const obj = { const obj = {
ifSingle: typeof v.fill === "string", ifSingle: typeof v.fill === "string",
level2Type: sourceData.level2Type, level2Type: sourceData.level2Type,
@@ -1173,7 +1163,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
angle: v.angle, angle: v.angle,
name: sourceData.name, name: sourceData.name,
priority: i + 1, priority: i + 1,
object:{ object: {
top: 0, top: 0,
left: 0, left: 0,
scaleX: 0,//对象的缩放比例 scaleX: 0,//对象的缩放比例
@@ -1192,8 +1182,8 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
let top = (v.top - (flTop - flHeight * flScaleY / 2)); let top = (v.top - (flTop - flHeight * flScaleY / 2));
let width = (v.width * v.scaleX); let width = (v.width * v.scaleX);
let height = (v.height * v.scaleY); let height = (v.height * v.scaleY);
if(v.originX === "center" && v.originY === "center") { if (v.originX === "center" && v.originY === "center") {
let {x:cx, y:cy} = calculateTopLeftPoint(width, height, left, top, v.angle); let { x: cx, y: cy } = calculateTopLeftPoint(width, height, left, top, v.angle);
left = cx; left = cx;
top = cy; top = cy;
} }
@@ -1205,18 +1195,18 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
obj.object.left = oX; obj.object.left = oX;
obj.object.scaleX = oScaleX; obj.object.scaleX = oScaleX;
obj.object.scaleY = oScaleY; obj.object.scaleY = oScaleY;
if(obj.ifSingle){ if (obj.ifSingle) {
// 单个的是从中心计算的 // 单个的是从中心计算的
let {x:cx, y:cy} = calculateCenterPoint(width, height, left, top, v.angle); let { x: cx, y: cy } = calculateCenterPoint(width, height, left, top, v.angle);
let oX = (cx-width/2) / flScaleX; let oX = (cx - width / 2) / flScaleX;
let oY = (cy-height/2) / flScaleY; let oY = (cy - height / 2) / flScaleY;
obj.location = [oX, oY]; obj.location = [oX, oY];
obj.scale = [oScaleX, oScaleY]; obj.scale = [oScaleX, oScaleY];
}else{ } else {
let fill = v.fill; let fill = v.fill;
let fill_ = v.fill_; let fill_ = v.fill_;
if(!fill || !fill_) return console.warn("印花元素不存在fill或fill_属性"); if (!fill || !fill_) return console.warn("印花元素不存在fill或fill_属性");
let {scale, angle} = getTransformScaleAngle(fill.patternTransform); let { scale, angle } = getTransformScaleAngle(fill.patternTransform);
let scaleX = scale * 5 * v.fill_.width / flWidth; let scaleX = scale * 5 * v.fill_.width / flWidth;
let scaleY = scale * 5 * v.fill_.height / flHeight; let scaleY = scale * 5 * v.fill_.height / flHeight;
let scaleXY = flWidth > flHeight ? scaleX : scaleY; let scaleXY = flWidth > flHeight ? scaleX : scaleY;
@@ -1231,16 +1221,16 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
obj.object.gapY = fill_.gapY; obj.object.gapY = fill_.gapY;
obj.object.fill_repeat = fill.repeat; obj.object.fill_repeat = fill.repeat;
} }
if(sourceData.type === "print"){ if (sourceData.type === "print") {
prints.push(obj); prints.push(obj);
}else if(sourceData.type === "trims"){ } else if (sourceData.type === "trims") {
trims.push(obj); trims.push(obj);
} }
}) })
// prints.sort((a, b) => a.ifSingle ? 1 : -1); // prints.sort((a, b) => a.ifSingle ? 1 : -1);
// prints.forEach((v, i) => v.priority = i + 1); // prints.forEach((v, i) => v.priority = i + 1);
// trims.forEach((v, i) => v.priority = i + 1); // trims.forEach((v, i) => v.priority = i + 1);
return {prints, trims}; return { prints, trims };
} }
@@ -1283,7 +1273,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
// 排除颜色图层和特殊组图层 // 排除颜色图层和特殊组图层
const excludedLayers = [SpecialLayerId.COLOR, SpecialLayerId.SPECIAL_GROUP]; const excludedLayers = [SpecialLayerId.COLOR, SpecialLayerId.SPECIAL_GROUP];
this.layers.value.forEach((layer) => { this.layers.value.forEach((layer) => {
if(excludedLayers.includes(layer.id)){ if (excludedLayers.includes(layer.id)) {
excludedLayers.push(...layer.children?.map((child) => child.id)); excludedLayers.push(...layer.children?.map((child) => child.id));
} }
}) })
@@ -1465,16 +1455,16 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
} }
} }
/** 修复JSON数据中的ID丢失问题 */ /** 修复JSON数据中的ID丢失问题 */
FixJsonIdLoss(json){ FixJsonIdLoss(json) {
const layerIds = []; const layerIds = [];
const layers = json?.layers || []; const layers = json?.layers || [];
const objects = json?.canvas?.objects || []; const objects = json?.canvas?.objects || [];
layers.forEach((layer) => { layers.forEach((layer) => {
layerIds.push(layer.id); layerIds.push(layer.id);
layer.children?.forEach((child) => layerIds.push(child.id)); layer.children?.forEach((child) => layerIds.push(child.id));
if(!layer.fabricObjects?.[0]?.id && !layer.fabricObject?.id){ if (!layer.fabricObjects?.[0]?.id && !layer.fabricObject?.id) {
const obj = objects?.find((o) => o.layerId === layer.id); const obj = objects?.find((o) => o.layerId === layer.id);
if(obj) { if (obj) {
layer.fabricObjects = [{ layer.fabricObjects = [{
id: obj.id, id: obj.id,
type: obj.type, type: obj.type,
@@ -1491,11 +1481,11 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
const excludedObjects = [SpecialLayerId.PART_SELECTOR]; const excludedObjects = [SpecialLayerId.PART_SELECTOR];
json.canvas.objects = objects.filter((v) => { json.canvas.objects = objects.filter((v) => {
// 指定ID排除 // 指定ID排除
if(excludedObjects.includes(v.id)) return false; if (excludedObjects.includes(v.id)) return false;
if(v.isBackground || v.isFixed) return true; if (v.isBackground || v.isFixed) return true;
// 当前图层不存在当前对象 // 当前图层不存在当前对象
if(!layerIds.includes(v.layerId)) return false; if (!layerIds.includes(v.layerId)) return false;
return true return true
}); });
} }
@@ -1507,22 +1497,22 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
*/ */
async createOtherLayers(otherData) { async createOtherLayers(otherData) {
if (!otherData) return console.warn("otherData 为空不需要添加"); if (!otherData) return console.warn("otherData 为空不需要添加");
let resolve = ()=>{}; let resolve = () => { };
this.awaitCanvasRun = ()=>(new Promise((v) => resolve = v)) this.awaitCanvasRun = () => (new Promise((v) => resolve = v))
const otherData_ = JSON.parse(JSON.stringify(otherData)); const otherData_ = JSON.parse(JSON.stringify(otherData));
console.log("==========创建其他图层", otherData_); console.log("==========创建其他图层", otherData_);
// 删除颜色图层和特殊组图层 // 删除颜色图层和特殊组图层
const ids = [SpecialLayerId.COLOR, SpecialLayerId.SPECIAL_GROUP]; const ids = [SpecialLayerId.COLOR, SpecialLayerId.SPECIAL_GROUP];
this.layers.value = this.layers.value.filter((layer) => { this.layers.value = this.layers.value.filter((layer) => {
if(ids.includes(layer.id)){ if (ids.includes(layer.id)) {
ids.push(...layer.children?.map((child) => child.id)); ids.push(...layer.children?.map((child) => child.id));
return false; return false;
} }
return true; return true;
}) })
this.canvas.getObjects().forEach((v) => { this.canvas.getObjects().forEach((v) => {
if(ids.includes(v.id) || ids.includes(v.layerId)){ if (ids.includes(v.id) || ids.includes(v.layerId)) {
this.canvas.remove(v) this.canvas.remove(v)
} }
}) })
@@ -1536,18 +1526,18 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
otherData_.printObject?.prints?.forEach((print, index) => {// 印花 otherData_.printObject?.prints?.forEach((print, index) => {// 印花
print.name = t("Canvas.Print") + (index + 1); print.name = t("Canvas.Print") + (index + 1);
print.type = "print"; print.type = "print";
if(print.ifSingle){ if (print.ifSingle) {
printTrimsLayers.unshift({...print}); printTrimsLayers.unshift({ ...print });
}else{ } else {
singleLayers.unshift({...print}); singleLayers.unshift({ ...print });
} }
}) })
otherData_.trims?.prints?.forEach((trims, index) => {// 元素 otherData_.trims?.prints?.forEach((trims, index) => {// 元素
trims.name = t("Canvas.Elements") + (index + 1); trims.name = t("Canvas.Elements") + (index + 1);
trims.type = "trims"; trims.type = "trims";
printTrimsLayers.unshift({...trims}); printTrimsLayers.unshift({ ...trims });
}) })
if(printTrimsLayers.length || singleLayers.length){ if (printTrimsLayers.length || singleLayers.length) {
await this.createPrintTrimsLayers(printTrimsLayers, singleLayers); await this.createPrintTrimsLayers(printTrimsLayers, singleLayers);
} }
await this.changeCanvas(); await this.changeCanvas();
@@ -1556,10 +1546,30 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
this.awaitCanvasRun = null; this.awaitCanvasRun = null;
} }
//设置印花元素颜色的裁剪信息
async setSpecialCliptInfo(isColor = true, isGroup = true) {
// 更新颜色层信息
if (isColor) {
const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
if (colorObject) {
await this.setObjecCliptInfo(colorObject);
}
}
// 更新特殊组图层信息
if (isGroup) {
const groupLayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
if (groupLayer) {
const groupRect = new fabric.Rect({});
await this.setObjecCliptInfo(groupRect);
groupLayer.clippingMask = groupRect.toObject();
}
}
}
// 设置画布对象的裁剪信息 // 设置画布对象的裁剪信息
async setObjecCliptInfo(tagObject, data){ async setObjecCliptInfo(tagObject, data) {
const fixedLayerObj = this.getFixedLayerObject(); const fixedLayerObj = this.getFixedLayerObject();
if(!fixedLayerObj) return console.warn("固定图层为空"); if (!fixedLayerObj) return console.warn("固定图层为空");
tagObject.set({ tagObject.set({
top: fixedLayerObj.top, top: fixedLayerObj.top,
left: fixedLayerObj.left, left: fixedLayerObj.left,
@@ -1572,7 +1582,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
}); });
var object = fixedLayerObj; var object = fixedLayerObj;
const imageUrl = this.props.clothingImageUrl2; const imageUrl = this.props.clothingImageUrl2;
if(imageUrl){ if (imageUrl) {
object = await new Promise((resolve, reject) => { object = await new Promise((resolve, reject) => {
fabric.Image.fromURL(imageUrl, (imgObject) => { fabric.Image.fromURL(imageUrl, (imgObject) => {
tagObject.set({ tagObject.set({
@@ -1592,8 +1602,8 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
}); });
tagObject.set('clipPath', transparentMask); tagObject.set('clipPath', transparentMask);
} }
async createColorLayer(color_){ async createColorLayer(color_) {
const color = color_ || {r:0,g:0,b:0,a:0}; const color = color_ || { r: 0, g: 0, b: 0, a: 0 };
// if(findLayer(this.layers.value, SpecialLayerId.COLOR)) { // if(findLayer(this.layers.value, SpecialLayerId.COLOR)) {
// return console.warn("画布中已存在颜色图层"); // return console.warn("画布中已存在颜色图层");
// } // }
@@ -1637,7 +1647,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
} }
// 创建印花和元素图层 // 创建印花和元素图层
async createPrintTrimsLayers(printTrimsLayers, singleLayers){ async createPrintTrimsLayers(printTrimsLayers, singleLayers) {
// if(findLayer(this.layers.value, SpecialLayerId.SPECIAL_GROUP)) { // if(findLayer(this.layers.value, SpecialLayerId.SPECIAL_GROUP)) {
// return console.warn("画布中已存在印花和元素组图层"); // return console.warn("画布中已存在印花和元素组图层");
// } // }
@@ -1651,12 +1661,12 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
const flScaleY = fixedLayerObj.scaleY const flScaleY = fixedLayerObj.scaleY
const children = []; const children = [];
// 添加印花和元素图层 // 添加印花和元素图层
for(let index = 0; index < printTrimsLayers.length; index++){ for (let index = 0; index < printTrimsLayers.length; index++) {
let item = printTrimsLayers[index]; let item = printTrimsLayers[index];
let id = generateId("layer_image_"); let id = generateId("layer_image_");
let name = item.name; let name = item.name;
let image = await new Promise(resolve => { let image = await new Promise(resolve => {
fabric.Image.fromURL(item.path, (fabricImage)=>{ fabric.Image.fromURL(item.path, (fabricImage) => {
resolve(fabricImage); resolve(fabricImage);
}, { crossOrigin: "anonymous" }); }, { crossOrigin: "anonymous" });
}) })
@@ -1664,7 +1674,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
let top = flTop - flHeight * flScaleY / 2 + (item.location?.[1] || 0) * flScaleY let top = flTop - flHeight * flScaleY / 2 + (item.location?.[1] || 0) * flScaleY
let scaleX = flWidth * (item.scale?.[0] || 1) / image.width * flScaleX let scaleX = flWidth * (item.scale?.[0] || 1) / image.width * flScaleX
let scaleY = flHeight * (item.scale?.[1] || 1) / image.height * flScaleY let scaleY = flHeight * (item.scale?.[1] || 1) / image.height * flScaleY
let {x, y} = calculateRotatedTopLeftDeg( let { x, y } = calculateRotatedTopLeftDeg(
image.width * scaleX, image.width * scaleX,
image.height * scaleY, image.height * scaleY,
left, left,
@@ -1679,11 +1689,11 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
let flipY = false; let flipY = false;
let blendMode = BlendMode.NORMAL; let blendMode = BlendMode.NORMAL;
// if(item.type === "trims") blendMode = BlendMode.NORMAL;// 元素正常 // if(item.type === "trims") blendMode = BlendMode.NORMAL;// 元素正常
if(item.object){ if (item.object) {
opacity = item.object.opacity opacity = item.object.opacity
flipX = item.object.flipX flipX = item.object.flipX
flipY = item.object.flipY flipY = item.object.flipY
if(item.object.blendMode) blendMode = item.object.blendMode; if (item.object.blendMode) blendMode = item.object.blendMode;
} }
image.set({ image.set({
left: x, left: x,
@@ -1714,18 +1724,18 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
isPrintTrims: true, isPrintTrims: true,
blendMode: blendMode, blendMode: blendMode,
fabricObjects: [image.toObject(["id", "layerId", "layerName"])], fabricObjects: [image.toObject(["id", "layerId", "layerName"])],
metadata: {sourceData: item}, metadata: { sourceData: item },
object: image, object: image,
}) })
children.push(layer); children.push(layer);
}; };
// 添加平铺图层 // 添加平铺图层
for(let index = 0; index < singleLayers.length; index++){ for (let index = 0; index < singleLayers.length; index++) {
let item = singleLayers[index]; let item = singleLayers[index];
let id = generateId("layer_image_"); let id = generateId("layer_image_");
let name = item.name; let name = item.name;
let image = await new Promise(resolve => { let image = await new Promise(resolve => {
fabric.Image.fromURL(item.path, (fabricImage)=>{ fabric.Image.fromURL(item.path, (fabricImage) => {
const imgElement = fabricImage.getElement(); const imgElement = fabricImage.getElement();
const tcanvas = document.createElement('canvas'); const tcanvas = document.createElement('canvas');
tcanvas.width = imgElement.width; tcanvas.width = imgElement.width;
@@ -1754,7 +1764,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
let flipY = false; let flipY = false;
let blendMode = BlendMode.NORMAL; let blendMode = BlendMode.NORMAL;
let fill_repeat = "repeat" let fill_repeat = "repeat"
if(item.object){ if (item.object) {
top += item.object.top * flScaleY top += item.object.top * flScaleY
left += item.object.left * flScaleX left += item.object.left * flScaleX
scaleX *= item.object.scaleX scaleX *= item.object.scaleX
@@ -1763,11 +1773,11 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
angle = item.object.angle angle = item.object.angle
flipX = item.object.flipX flipX = item.object.flipX
flipY = item.object.flipY flipY = item.object.flipY
if(item.object.blendMode) blendMode = item.object.blendMode; if (item.object.blendMode) blendMode = item.object.blendMode;
gapX = item.object.gapX gapX = item.object.gapX
gapY = item.object.gapY gapY = item.object.gapY
fillSource = imageAddGapToCanvas(image, gapX, gapY); fillSource = imageAddGapToCanvas(image, gapX, gapY);
if(item.object.fill_repeat) fill_repeat = item.object.fill_repeat; if (item.object.fill_repeat) fill_repeat = item.object.fill_repeat;
} }
let rect = new fabric.Rect({ let rect = new fabric.Rect({
id: id, id: id,
@@ -1791,7 +1801,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
offsetX: offsetX, // 水平偏移 offsetX: offsetX, // 水平偏移
offsetY: offsetY, // 垂直偏移 offsetY: offsetY, // 垂直偏移
}), }),
fill_ : { fill_: {
source: item.path, source: item.path,
gapX: gapX, gapX: gapX,
gapY: gapY, gapY: gapY,
@@ -1811,7 +1821,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
isPrintTrims: true, isPrintTrims: true,
blendMode: blendMode, blendMode: blendMode,
fabricObjects: [rect.toObject(["id", "layerId", "layerName"])], fabricObjects: [rect.toObject(["id", "layerId", "layerName"])],
metadata: {sourceData: item}, metadata: { sourceData: item },
object: rect, object: rect,
}) })
children.push(layer); children.push(layer);
@@ -1828,9 +1838,9 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
// }) // })
// children.push(layer); // children.push(layer);
// } // }
if(children.length === 0) return; if (children.length === 0) return;
// 印花元素排序 // 印花元素排序
if(new Set(children.map(v => v.metadata.sourceData.priority)).size === children.length){ if (new Set(children.map(v => v.metadata.sourceData.priority)).size === children.length) {
children.sort((a, b) => b.metadata.sourceData.priority - a.metadata.sourceData.priority); children.sort((a, b) => b.metadata.sourceData.priority - a.metadata.sourceData.priority);
} }
children.forEach(layer => { children.forEach(layer => {
@@ -1858,21 +1868,21 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
/** /**
* 画布事件变更后 * 画布事件变更后
*/ */
async changeCanvas(fids = [], isBeforeChange = false){ async changeCanvas(fids = [], isBeforeChange = false) {
if(!isBeforeChange) this.canvasChangeing = false; if (!isBeforeChange) this.canvasChangeing = false;
const fixedLayerObj = this.getFixedLayerObject(); const fixedLayerObj = this.getFixedLayerObject();
if(!fixedLayerObj) return console.warn("固定图层对象不存在", fixedLayerObj) if (!fixedLayerObj) return console.warn("固定图层对象不存在", fixedLayerObj)
const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR); const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
if(colorObject){ if (colorObject) {
const ids = this.layerManager.getBlendModeLayerIds(SpecialLayerId.SPECIAL_GROUP).filter(id => !fids.includes(id)); const ids = this.layerManager.getBlendModeLayerIds(SpecialLayerId.SPECIAL_GROUP).filter(id => !fids.includes(id));
if(ids.length === 0){ if (ids.length === 0) {
ids.unshift(SpecialLayerId.SPECIAL_GROUP); ids.unshift(SpecialLayerId.SPECIAL_GROUP);
await this.setObjecCliptInfo(colorObject); await this.setObjecCliptInfo(colorObject);
this.canvas.renderAll(); this.canvas.renderAll();
return; return;
} }
const base64 = await this.exportManager.exportImage({layerIdArray2: ids, isEnhanceImg: true}); const base64 = await this.exportManager.exportImage({ layerIdArray2: ids, isEnhanceImg: true });
if(!base64) return console.warn("导出图片失败", base64) if (!base64) return console.warn("导出图片失败", base64)
const canvas = await base64ToCanvas(base64, fixedLayerObj.scaleX * 2, true); const canvas = await base64ToCanvas(base64, fixedLayerObj.scaleX * 2, true);
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
const width = fixedLayerObj.width; const width = fixedLayerObj.width;
@@ -1885,15 +1895,15 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
} }
} }
/** 画布变更之前 */ /** 画布变更之前 */
async beforeChangeCanvas(objects){ async beforeChangeCanvas(objects) {
if(this.canvasChangeing) return; if (this.canvasChangeing) return;
const ids = objects.filter(v => { const ids = objects.filter(v => {
return v.isPrintTrims && v.globalCompositeOperation && v.globalCompositeOperation !== BlendMode.NORMAL return v.isPrintTrims && v.globalCompositeOperation && v.globalCompositeOperation !== BlendMode.NORMAL
}).map(v => v.layerId); }).map(v => v.layerId);
if(ids.length > 0){ if (ids.length > 0) {
this.canvasChangeing = true; this.canvasChangeing = true;
this.canvas.getObjects().forEach(v => { this.canvas.getObjects().forEach(v => {
if(ids.includes(v.layerId)){ if (ids.includes(v.layerId)) {
v.globalCompositeOperation_ = v.globalCompositeOperation; v.globalCompositeOperation_ = v.globalCompositeOperation;
v.globalCompositeOperation = BlendMode.NORMAL; v.globalCompositeOperation = BlendMode.NORMAL;
} }
@@ -2103,13 +2113,13 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
moveActiveObject(direction, step = 1) { moveActiveObject(direction, step = 1) {
const objects = []; const objects = [];
const activeObject = this.canvas.getActiveObject(); const activeObject = this.canvas.getActiveObject();
if(!activeObject) return; if (!activeObject) return;
const initPos = { const initPos = {
id: activeObject.id, id: activeObject.id,
left: activeObject.left, left: activeObject.left,
top: activeObject.top, top: activeObject.top,
}; };
switch(direction) { switch (direction) {
case "up": case "up":
activeObject.top -= step; activeObject.top -= step;
break; break;
@@ -2123,7 +2133,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
activeObject.left += step; activeObject.left += step;
break; break;
} }
if(!activeObject.id) return this.canvas.renderAll(); if (!activeObject.id) return this.canvas.renderAll();
const cmd = new ObjectMoveCommand({ const cmd = new ObjectMoveCommand({
canvas: this.canvas, canvas: this.canvas,
initPos, initPos,

View File

@@ -606,7 +606,9 @@ export class ExportManager {
imageSmoothingEnabled: true, imageSmoothingEnabled: true,
}); });
// tempFabricCanvas.setZoom(1); // tempFabricCanvas.setZoom(1);
console.log("==========", fixedLayerObject) const ox = fixedLayerObject.left - fixedLayerObject.width * fixedLayerObject.scaleX / 2
const oy = fixedLayerObject.top - fixedLayerObject.height * fixedLayerObject.scaleY / 2
// console.log("==========", fixedLayerObject, ox, oy)
try { try {
// 克隆并添加所有对象到临时画布,需要调整位置相对于固定图层 // 克隆并添加所有对象到临时画布,需要调整位置相对于固定图层
for (let i = 0; i < objectsToExport.length; i++) { for (let i = 0; i < objectsToExport.length; i++) {
@@ -616,15 +618,16 @@ export class ExportManager {
restoreOpacityInRedGreen && true restoreOpacityInRedGreen && true
); );
if (cloned) { if (cloned) {
let scaleX = cloned.scaleX / fixedLayerObject.scaleX
let scaleY = cloned.scaleY / fixedLayerObject.scaleY
let top = (cloned.top - oy) * scaleY
let left = (cloned.left - ox) * scaleX
cloned.set({ cloned.set({
left: canvasWidth / 2, left: left,
top: canvasHeight / 2, top: top,
scaleX: cloned.scaleX / fixedLayerObject.scaleX, scaleX: scaleX,
scaleY: cloned.scaleY / fixedLayerObject.scaleY, scaleY: scaleY,
originX: "center",
originY: "center",
}); });
console.log("==========", {...cloned})
// 更新对象坐标 // 更新对象坐标
cloned.setCoords(); cloned.setCoords();
tempFabricCanvas.add(cloned); tempFabricCanvas.add(cloned);

View File

@@ -180,7 +180,7 @@ export class CommandManager {
this._recordPerformance("execute", command.constructor.name, duration); this._recordPerformance("execute", command.constructor.name, duration);
// 通知状态变化 // 通知状态变化
this._notifyStateChange(); this._notifyStateChange("execute");
console.log(`✅ 命令执行成功: ${command.constructor.name}`); console.log(`✅ 命令执行成功: ${command.constructor.name}`);
return result; return result;
@@ -219,7 +219,7 @@ export class CommandManager {
this._recordPerformance("undo", command.constructor.name, duration); this._recordPerformance("undo", command.constructor.name, duration);
// 通知状态变化 // 通知状态变化
this._notifyStateChange(); this._notifyStateChange("undo");
console.log(`✅ 命令撤销成功: ${command.constructor.name}`); console.log(`✅ 命令撤销成功: ${command.constructor.name}`);
return result; return result;
@@ -258,7 +258,7 @@ export class CommandManager {
this._recordPerformance("redo", command.constructor.name, duration); this._recordPerformance("redo", command.constructor.name, duration);
// 通知状态变化 // 通知状态变化
this._notifyStateChange(); this._notifyStateChange("redo");
console.log(`✅ 命令重做成功: ${command.constructor.name}`); console.log(`✅ 命令重做成功: ${command.constructor.name}`);
return result; return result;
@@ -298,7 +298,7 @@ export class CommandManager {
this.undoStack = []; this.undoStack = [];
this.redoStack = []; this.redoStack = [];
this._notifyStateChange(); this._notifyStateChange("clear");
// console.log("📝 命令历史已清空"); // console.log("📝 命令历史已清空");
} }
@@ -417,10 +417,12 @@ export class CommandManager {
* 通知状态变化 * 通知状态变化
* @private * @private
*/ */
_notifyStateChange() { _notifyStateChange(type) {
if (this.onStateChange) { if (this.onStateChange) {
try { try {
this.onStateChange(this.getState()); const obj = this.getState();
obj.type = type;
this.onStateChange(obj);
} catch (error) { } catch (error) {
console.error("状态变化回调执行失败:", error); console.error("状态变化回调执行失败:", error);
} }

View File

@@ -85,7 +85,7 @@ export class LiquifyRealTimeUpdater {
if (isDrawing && this.config.useDirectUpdate) { if (isDrawing && this.config.useDirectUpdate) {
// 拖拽过程中使用快速更新(降低质量以提高性能) // 拖拽过程中使用快速更新(降低质量以提高性能)
this._fastUpdate(imageData); await this._fastUpdate(imageData);
} else { } else {
// 拖拽结束后使用完整更新(最高质量) // 拖拽结束后使用完整更新(最高质量)
await this._fullUpdate(imageData); await this._fullUpdate(imageData);
@@ -124,7 +124,7 @@ export class LiquifyRealTimeUpdater {
* @param {ImageData} imageData 图像数据 * @param {ImageData} imageData 图像数据
* @private * @private
*/ */
_fastUpdate(imageData) { async _fastUpdate(imageData) {
if (!this.targetObject || !this.targetObject._element) { if (!this.targetObject || !this.targetObject._element) {
return; return;
} }
@@ -138,12 +138,14 @@ export class LiquifyRealTimeUpdater {
// 直接更新fabric对象的图像源使用PNG格式保持质量 // 直接更新fabric对象的图像源使用PNG格式保持质量
const targetElement = this.targetObject._element; const targetElement = this.targetObject._element;
// 方案1: 直接设置src属性最高性能 // 方案1: 直接设置src属性最高性能
const dataURL = this.tempCanvas.toDataURL("image/png", quality); const dataURL = this.tempCanvas.toDataURL("image/png", quality);
if (targetElement.src !== dataURL) { if (targetElement.src !== dataURL) {
targetElement.src = dataURL; // targetElement.src = dataURL;
const image = new Image();
image.src = dataURL;
await image.decode();
this.targetObject.setElement(image);
// 关键优化直接设置fabric对象为脏状态但不立即渲染 // 关键优化直接设置fabric对象为脏状态但不立即渲染
// this.targetObject.dirty = false; // 标记为不需要立即渲染 // this.targetObject.dirty = false; // 标记为不需要立即渲染

View File

@@ -447,6 +447,7 @@
</template> </template>
</CanvasEditor> </CanvasEditor>
</div> </div>
<img src="" alt="" id="canvas-test-dom">
</div> </div>
</template> </template>
<style> <style>
@@ -458,6 +459,13 @@
height: 600px !important; height: 600px !important;
z-index: 99999999; z-index: 99999999;
} }
#canvas-test-dom{
position: fixed;
z-index: 9999999999;
top: 0;
left: 0;
pointer-events: none;
}
</style> </style>
<style scoped lang="less"> <style scoped lang="less">
* { * {

View File

@@ -247,6 +247,7 @@ export default defineComponent({
let front = detailData.frontBack.front[detailData.imgDomIndex] let front = detailData.frontBack.front[detailData.imgDomIndex]
let back = detailData.frontBack.back[detailData.imgDomIndex] let back = detailData.frontBack.back[detailData.imgDomIndex]
store.commit('DesignDetail/updataDetailItem',{maskUrl:value}) store.commit('DesignDetail/updataDetailItem',{maskUrl:value})
await nextTick()
if(!detailData.selectDetail.partialDesign.partialDesignPath && !detailData.selectDetail.partialDesign.partialDesignBase64){ if(!detailData.selectDetail.partialDesign.partialDesignPath && !detailData.selectDetail.partialDesign.partialDesignBase64){
await privewDetail() await privewDetail()
}else{ }else{

View File

@@ -77,19 +77,10 @@ export default defineComponent({
left: 50%; left: 50%;
top: 50%; top: 50%;
transform: translate(-50%,-50%); transform: translate(-50%,-50%);
// width: 80%;
// height: auto;
// max-height: 80vh;
// width: max-content;
position: absolute; position: absolute;
video{ video{
// width: 100%; max-height:80vh;
// max-height: 80vh; max-width:80vw;
// height: 100%;
// object-fit: contain;
// width: max-content;
max-height: 80vh;
max-width: 80vw;
} }
.general_video_btn{ .general_video_btn{
color: #fff; color: #fff;

View File

@@ -41,10 +41,10 @@
<div class="center">{{ selectObject?.styleName?selectObject?.styleName:$t('Header.All') }}</div> <div class="center">{{ selectObject?.styleName?selectObject?.styleName:$t('Header.All') }}</div>
<div class="gallery_btn" @click="setStyle">{{ $t('Habit.Select') }}</div> <div class="gallery_btn" @click="setStyle">{{ $t('Habit.Select') }}</div>
</div> </div>
<div class="style brand marginBottom"> <!-- <div class="style brand marginBottom">
<div class="text">{{$t('Habit.Brand')}}:</div> <div class="text">{{$t('Habit.Brand')}}:</div>
<div class="gallery_btn" @click="setBrandDNA">{{ $t('Habit.Select') }}</div> <div class="gallery_btn" @click="setBrandDNA">{{ $t('Habit.Select') }}</div>
</div> </div> -->
<div class="brandImg" v-if="selectObject.userBrandDna"><img :src="selectObject.userBrandDnaImg"></div> <div class="brandImg" v-if="selectObject.userBrandDna"><img :src="selectObject.userBrandDnaImg"></div>
<div class="brandDNAStrenght marginBottom" v-if="selectObject.userBrandDna"> <div class="brandDNAStrenght marginBottom" v-if="selectObject.userBrandDna">
<div class="text" style="font-size: 1.6rem;"> <div class="text" style="font-size: 1.6rem;">

View File

@@ -340,12 +340,12 @@ const navTypeList = (t)=>{
value:'Models', value:'Models',
router:'library=Models' router:'library=Models'
}, },
{ // {
icon:'fi-ss-gem', // icon:'fi-ss-gem',
label:t('LibraryPage.brandDNA'), // label:t('LibraryPage.brandDNA'),
value:'MyBrand', // value:'MyBrand',
router:'library=MyBrand' // router:'library=MyBrand'
}, // },
] ]
}, },
// history:{ // history:{