导出印花等所有信息

This commit is contained in:
李志鹏
2026-01-06 14:17:04 +08:00
parent 73aca07391
commit 466d278b29
12 changed files with 301 additions and 111 deletions

View File

@@ -80,6 +80,8 @@ export class FillRepeatCommand extends Command {
source: FillSourceToBase64(img), source: FillSourceToBase64(img),
gapX: 0, gapX: 0,
gapY: 0, gapY: 0,
width: img.width,
height: img.height,
}; };
const bgObject = this.canvasManager.getBackgroundLayerObject(); const bgObject = this.canvasManager.getBackgroundLayerObject();
const pattern = new fabric.Pattern({ const pattern = new fabric.Pattern({
@@ -275,6 +277,8 @@ export class FillRepeatGapChangeCommand extends Command {
const image = new Image(); const image = new Image();
image.src = object.fill_.source; image.src = object.fill_.source;
await image.decode(); await image.decode();
object.fill_.width = image.width;
object.fill_.height = image.height;
// 创建透明 Canvas // 创建透明 Canvas
const tcanvas = document.createElement('canvas'); const tcanvas = document.createElement('canvas');
tcanvas.width = image.width + object.fill_.gapX; tcanvas.width = image.width + object.fill_.gapX;

View File

@@ -524,6 +524,7 @@ export class RemoveLayerCommand extends Command {
this.layerId = options.layerId; this.layerId = options.layerId;
this.activeLayerId = options.activeLayerId; this.activeLayerId = options.activeLayerId;
this.layerManager = options.layerManager || null; this.layerManager = options.layerManager || null;
this.IsOnlyLayer = this.layers.value.filter((v => !v.isFixed && !v.isFixedOther && !v.isBackground)).length <= 1
// 查找要删除的图层 // 查找要删除的图层
this.layerIndex = this.layers.value.findIndex( this.layerIndex = this.layers.value.findIndex(
@@ -600,7 +601,9 @@ export class RemoveLayerCommand extends Command {
); );
// 从图层列表中删除 // 从图层列表中删除
this.layers.value.splice(this.layerIndex, 1); this.layers.value.splice(this.layerIndex, 1);
if(this.IsOnlyLayer){
this.addCmd = await this.layerManager?.createLayer?.(null, LayerType.EMPTY, {}, false);
}
// 如果删除的是当前活动图层,需要更新活动图层 // 如果删除的是当前活动图层,需要更新活动图层
if (this.isActiveLayer) { if (this.isActiveLayer) {
// 查找最近的非背景层作为新的活动图层 // 查找最近的非背景层作为新的活动图层
@@ -633,6 +636,9 @@ export class RemoveLayerCommand extends Command {
async undo() { async undo() {
// 恢复图层到原位置 // 恢复图层到原位置
if (this.layerIndex !== -1 && this.removedLayer) { if (this.layerIndex !== -1 && this.removedLayer) {
if(this.IsOnlyLayer && this.addCmd){
this.addCmd?.undo?.();
}
this.layers.value.splice(this.layerIndex, 0, this.removedLayer); this.layers.value.splice(this.layerIndex, 0, this.removedLayer);
// 使用优化渲染批处理恢复真实对象到画布 // 使用优化渲染批处理恢复真实对象到画布
@@ -650,7 +656,6 @@ export class RemoveLayerCommand extends Command {
} }
}); });
}); });
await this.layerManager?.updateLayersObjectsInteractivity?.(); await this.layerManager?.updateLayersObjectsInteractivity?.();
this.canvas.renderAll(); this.canvas.renderAll();
@@ -4286,24 +4291,28 @@ export class RemoveChildLayerCommand extends Command {
} }
// 恢复子图层到原位置 // 恢复子图层到原位置
this.parentLayer.children.splice(this.childIndex, 0, this.removedChild); this.parentLayer.children.splice(this.childIndex, 0, this.removedChild);
optimizeCanvasRendering(this.canvas, async () => { await new Promise((resolve) => {
this.originalObjects.forEach((obj) => { optimizeCanvasRendering(this.canvas, async () => {
// 恢复对象到画布 this.originalObjects.forEach((obj) => {
this.canvas.add(obj); // 恢复对象到画布
// 恢复对象的图层信息 this.canvas.add(obj);
obj.layerId = this.layerId; // 恢复对象的图层信息
obj.layerName = this.removedChild.name; obj.layerId = this.layerId;
obj.setCoords(); // 更新坐标 obj.layerName = this.removedChild.name;
}); obj.setCoords(); // 更新坐标
});
// 如果是原活动图层,恢复活动图层 // 如果是原活动图层,恢复活动图层
if (this.isActiveLayer) { if (this.isActiveLayer) {
this.activeLayerId.value = this.layerId; this.activeLayerId.value = this.layerId;
} }
// 重新渲染画布 // 重新渲染画布
await this.layerManager?.updateLayersObjectsInteractivity(false); await this.layerManager?.updateLayersObjectsInteractivity(false);
resolve(true);
});
}); });
return true;
} }
getInfo() { getInfo() {
@@ -4501,6 +4510,7 @@ export class SetColorLayerFillCommand extends Command {
this.oldFill = JSON.parse(JSON.stringify(this.object.fill)); this.oldFill = JSON.parse(JSON.stringify(this.object.fill));
this.layer.blendMode = "multiply"; this.layer.blendMode = "multiply";
this.object.set("globalCompositeOperation", "multiply"); this.object.set("globalCompositeOperation", "multiply");
this.object.set("originColor", options.originColor);
} }
async execute(isUndo = false) { async execute(isUndo = false) {

View File

@@ -364,14 +364,13 @@ const clickColor = () => {
const fill = layerObject.value.fill; const fill = layerObject.value.fill;
if (fill) { if (fill) {
const obj = fillToPallet(fill); const obj = fillToPallet(fill);
console.log("===========:", obj);
palletPanel(obj).then((res) => { palletPanel(obj).then((res) => {
console.log("===========:", res);
const cmd = new SetColorLayerFillCommand({ const cmd = new SetColorLayerFillCommand({
canvas: canvasManager.canvas, canvas: canvasManager.canvas,
layerManager: layerManager, layerManager: layerManager,
object: layerObject.value, object: layerObject.value,
newFill: palletToFill(res), newFill: palletToFill(res),
originColor: res,
}); });
layerManager.commandManager.execute(cmd); layerManager.commandManager.execute(cmd);
}); });

View File

@@ -264,7 +264,7 @@
* 显示面板 * 显示面板
*/ */
function show() { function show() {
if (activeObjects.length === 0) return; if (activeObjects.value.length === 0) return;
visible.value = true; visible.value = true;
closePanel.value = true; closePanel.value = true;
} }
@@ -276,14 +276,14 @@
visible.value = false; visible.value = false;
} }
// 获取当前选中的对象 // 获取当前选中的对象
const activeObjects = reactive([]); const activeObjects = ref([]);
const getActiveObject = (e) => { const getActiveObject = (e) => {
console.log("==========切换激活对象", e, activeObjects); console.log("==========切换激活对象", e, activeObjects);
activeObjects.splice(0, activeObjects.length, ...e.selected); activeObjects.value = [...e.selected];
activeObjects.forEach((v) => { activeObjects.value.forEach((v) => {
v.layer = props.layerManager.getLayerById(v.layerId); v.layer = props.layerManager.getLayerById(v.layerId);
}); });
if (activeObjects.length === 0) { if (activeObjects.value.length === 0) {
close(); close();
} else { } else {
show(); show();
@@ -291,7 +291,7 @@
}; };
//取消当前选中 //取消当前选中
const cancelSelect = () => { const cancelSelect = () => {
activeObjects.splice(0, activeObjects.length); activeObjects.value = [];
close(); close();
}; };
const lastSelectLayerId = inject("lastSelectLayerId"); const lastSelectLayerId = inject("lastSelectLayerId");
@@ -537,7 +537,7 @@
// 更新选中对象属性 // 更新选中对象属性
const updateActiveObjects = (arrs, keys, isNumber = true) => { const updateActiveObjects = (arrs, keys, isNumber = true) => {
arrs.forEach((v) => { arrs.forEach((v) => {
activeObjects.forEach((item) => { activeObjects.value.forEach((item) => {
if (item.id === v.id) { if (item.id === v.id) {
keys.forEach( keys.forEach(
(key) => (item[key] = isNumber ? Number(v[key]) : v[key]) (key) => (item[key] = isNumber ? Number(v[key]) : v[key])
@@ -545,6 +545,7 @@
} }
}); });
}); });
activeObjects.value = [...activeObjects.value];
}; };
// 旋转对象时更新角度 // 旋转对象时更新角度

View File

@@ -1009,6 +1009,7 @@ defineExpose({
exportImage: ({ exportImage: ({
isContainBg = false, // 是否包含背景图层 isContainBg = false, // 是否包含背景图层
isContainFixed = false, // 是否包含固定图层 isContainFixed = false, // 是否包含固定图层
isContainFixedOther = false, // 是否包含其他固定图层
isCropByBg = false, // 是否使用背景大小裁剪 // 如果为true则导出时裁剪到背景图层大小 isCropByBg = false, // 是否使用背景大小裁剪 // 如果为true则导出时裁剪到背景图层大小
layerId = "", // 导出具体图层ID layerId = "", // 导出具体图层ID
layerIdArray = [], // 导出多个图层ID数组 layerIdArray = [], // 导出多个图层ID数组
@@ -1018,6 +1019,7 @@ defineExpose({
return canvasManager.exportImage({ return canvasManager.exportImage({
isContainBg, isContainBg,
isContainFixed, isContainFixed,
isContainFixedOther,
isCropByBg, isCropByBg,
layerId, layerId,
layerIdArray, layerIdArray,
@@ -1047,6 +1049,14 @@ defineExpose({
return result; return result;
}, },
/**
* 导出所有信息
* @returns {Object} 包含所有图层信息的对象
*/
exportAllInfo: () => {
return canvasManager.exportAllInfo();
},
/** /**
* 拖拽排序图层 * 拖拽排序图层
* @param {number} oldIndex 原索引 * @param {number} oldIndex 原索引

View File

@@ -30,7 +30,9 @@ import {
palletToFill, palletToFill,
fillToCssStyle, fillToCssStyle,
calculateRotatedTopLeftDeg, calculateRotatedTopLeftDeg,
calculateCenterPoint,
createPatternTransform, createPatternTransform,
getTransformScaleAngle,
base64ToCanvas, base64ToCanvas,
} from "../utils/helper"; } from "../utils/helper";
import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands"; import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands";
@@ -566,10 +568,10 @@ export class CanvasManager {
} }
// 更新颜色层信息 // 更新颜色层信息
// const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR); const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
// if(colorObject){ if(colorObject){
// await this.setObjecCliptInfo(colorObject); await this.setObjecCliptInfo(colorObject);
// } }
const groupLayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP); const groupLayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
if(groupLayer){ if(groupLayer){
const groupRect = new fabric.Rect({}); const groupRect = new fabric.Rect({});
@@ -808,6 +810,13 @@ export class CanvasManager {
return layerObjectByLayerId; return layerObjectByLayerId;
} }
getObjectsByIds(ids){
const objects = this.canvas.getObjects().filter((obj) => {
return ids.includes(obj.id);
});
return objects;
}
/** /**
* 更新蒙层位置 * 更新蒙层位置
* @param {Object} backgroundLayerObject 背景层对象 * @param {Object} backgroundLayerObject 背景层对象
@@ -936,6 +945,7 @@ export class CanvasManager {
options.restoreOpacityInRedGreen !== undefined options.restoreOpacityInRedGreen !== undefined
? options.restoreOpacityInRedGreen ? options.restoreOpacityInRedGreen
: false, // 默认在红绿图模式下恢复透明度 : false, // 默认在红绿图模式下恢复透明度
excludedLayers: [SpecialLayerId.SPECIAL_GROUP],
}; };
// 如果在红绿图模式下且没有指定具体的图层,自动包含所有普通图层 // 如果在红绿图模式下且没有指定具体的图层,自动包含所有普通图层
@@ -966,6 +976,22 @@ export class CanvasManager {
} }
} }
/**
* 导出所有信息
* @returns {Object} 包含所有图层信息的对象
*/
async exportAllInfo() {
// 导出颜色图层信息
const color = await this.exportColorLayer().catch(() => (null));
// 导出印花和元素图层信息
const printTrimsData = await this.exportPrintTrimsLayers().catch(() => ({prints: null, trims: null}));
return {
color,
...printTrimsData,
};
}
/** /**
* 导出颜色图层 * 导出颜色图层
* @returns {Object} 导出的颜色图层数据URL * @returns {Object} 导出的颜色图层数据URL
@@ -980,13 +1006,12 @@ export class CanvasManager {
console.warn("颜色图层不存在,请确保已添加颜色图层"); console.warn("颜色图层不存在,请确保已添加颜色图层");
return Promise.reject("颜色图层不存在"); return Promise.reject("颜色图层不存在");
} }
const color = fillToCssStyle(object.fill) const css = fillToCssStyle(object.fill)
const canvas = new fabric.StaticCanvas(); const canvas = new fabric.StaticCanvas();
canvas.setDimensions({ canvas.setDimensions({
width: object.width, width: object.width,
height: object.height, height: object.height,
backgroundColor: null, backgroundColor: null,
// enableRetinaScaling: true,
imageSmoothingEnabled: true, imageSmoothingEnabled: true,
}); });
const cloneObject = await new Promise((resolve, reject) => { const cloneObject = await new Promise((resolve, reject) => {
@@ -1007,9 +1032,75 @@ export class CanvasManager {
quality: 1, quality: 1,
}); });
canvas.clear(); canvas.clear();
return {color, base64}; const color = object.originColor;
return {css, base64, color};
} }
/**
* 导出印花和元素图层
*/
async exportPrintTrimsLayers() {
const object = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
if(!object) return Promise.reject("印花和元素图层组不存在");
const ids = object.children.map((v) => v.id);
const objects = this.getObjectsByIds(ids).filter((v) => !!v.sourceData);
const fixedLayerObj = this.getFixedLayerObject();
if(!fixedLayerObj) return Promise.reject("固定图层不存在");
const flWidth = fixedLayerObj.width
const flHeight = fixedLayerObj.height
const flTop = fixedLayerObj.top
const flLeft = fixedLayerObj.left
const flScaleX = fixedLayerObj.scaleX
const flScaleY = fixedLayerObj.scaleY
const prints = [];
const trims = [];
objects.forEach((v) => {
const obj = {
ifSingle: v.sourceData.ifSingle,
level2Type: v.sourceData.level2Type,
designType: v.sourceData.designType,
path: v.sourceData.path,
location: [0, 0],
scale: [0, 0],
angle: v.angle,
name: v.sourceData.name,
}
if(obj.ifSingle){
let left = (v.left - (flLeft - flWidth * flScaleX / 2));
let top = (v.top - (flTop - flHeight * flScaleY / 2));
let width = (v.width * v.scaleX);
let height = (v.height * v.scaleY);
let {x:cx, y:cy} = calculateCenterPoint(width, height, left, top, v.angle);
let x = (cx-width/2) / flScaleX;
let y = (cy-height/2) / flScaleY;
obj.location = [x, y];
obj.scale = [(v.width * v.scaleX) / (flWidth * flScaleX), (v.height * v.scaleY) / (flHeight * flScaleY)];
}else{
let fill = v.fill;
let fill_ = v.fill_;
if(!fill || !fill_) return;
let {scale, angle} = getTransformScaleAngle(fill.patternTransform);
let scaleX = scale * 5 * v.fill_.width / flWidth;
let scaleY = scale * 5 * v.fill_.height / flHeight;
let scaleXY = flWidth > flHeight ? scaleX : scaleY;
let left = fill.offsetX + v.fill_.width * scale / 2;
let top = fill.offsetY + v.fill_.height * scale / 2;
obj.scale = [scaleXY, scaleXY];
obj.angle = angle;
obj.location = [left, top];
}
if(obj.level2Type === "Pattern"){
prints.push(obj);
}else if(obj.level2Type === "Embroidery"){
trims.push(obj);
}
})
return {prints, trims};
}
dispose() { dispose() {
// 释放导出管理器资源 // 释放导出管理器资源
if (this.exportManager) { if (this.exportManager) {
@@ -1126,6 +1217,12 @@ export class CanvasManager {
"erasable", "erasable",
"customType", "customType",
"fill_", "fill_",
"scaleX",
"scaleY",
"top",
"left",
"width",
"height",
]), ]),
layers: simplifyLayersData, // 简化图层数据 layers: simplifyLayersData, // 简化图层数据
// layers: JSON.stringify(JSON.parse(JSON.stringify(this.layers.value))), // 全数据 // layers: JSON.stringify(JSON.parse(JSON.stringify(this.layers.value))), // 全数据
@@ -1300,9 +1397,9 @@ export class CanvasManager {
singleLayers.unshift({...print}); singleLayers.unshift({...print});
} }
}) })
otherData_?.trims?.prints?.forEach((print, index) => { otherData_?.trims?.prints?.forEach((trims, index) => {
print.name = t("Canvas.Elements") + (index + 1); trims.name = t("Canvas.Elements") + (index + 1);
printTrimsLayers.unshift({...print}); printTrimsLayers.unshift({...trims});
}) })
await this.createPrintTrimsLayers(printTrimsLayers, singleLayers); await this.createPrintTrimsLayers(printTrimsLayers, singleLayers);
} }
@@ -1350,7 +1447,6 @@ export class CanvasManager {
if(!color) return console.warn("颜色为空不需要添加"); if(!color) return console.warn("颜色为空不需要添加");
if(findLayer(this.layers.value, SpecialLayerId.COLOR)) return console.warn("画布中已存在颜色图层"); if(findLayer(this.layers.value, SpecialLayerId.COLOR)) return console.warn("画布中已存在颜色图层");
console.log("==========添加颜色图层", color, this.layers.value.length) console.log("==========添加颜色图层", color, this.layers.value.length)
const fixedLayerObj = this.getFixedLayerObject();
// 创建颜色图层对象 // 创建颜色图层对象
const colorRect = new fabric.Rect({ const colorRect = new fabric.Rect({
id: SpecialLayerId.COLOR, id: SpecialLayerId.COLOR,
@@ -1359,8 +1455,9 @@ export class CanvasManager {
isVisible: true, isVisible: true,
isLocked: true, isLocked: true,
globalCompositeOperation: BlendMode.MULTIPLY, globalCompositeOperation: BlendMode.MULTIPLY,
originColor: color,
}); });
// await this.setObjecCliptInfo(colorRect); await this.setObjecCliptInfo(colorRect);
const gradientObj = palletToFill(color); const gradientObj = palletToFill(color);
const gradient = new fabric.Gradient({ const gradient = new fabric.Gradient({
type: 'linear', type: 'linear',
@@ -1398,24 +1495,24 @@ export class CanvasManager {
const children = []; const children = [];
// 添加印花和元素图层 // 添加印花和元素图层
for(let index = 0; index < printTrimsLayers.length; index++){ for(let index = 0; index < printTrimsLayers.length; index++){
let print = printTrimsLayers[index]; let item = printTrimsLayers[index];
let id = generateId("layer_image_"); let id = generateId("layer_image_");
let name = print.name; let name = item.name;
let image = await new Promise(resolve => { let image = await new Promise(resolve => {
fabric.Image.fromURL(print.path, (fabricImage)=>{ fabric.Image.fromURL(item.path, (fabricImage)=>{
const left = flLeft - flWidth * flScaleX / 2 + (print.location?.[0] || 0) * flScaleX const left = flLeft - flWidth * flScaleX / 2 + (item.location?.[0] || 0) * flScaleX
const top = flTop - flHeight * flScaleY / 2 + (print.location?.[1] || 0) * flScaleY const top = flTop - flHeight * flScaleY / 2 + (item.location?.[1] || 0) * flScaleY
const scaleX = flWidth * (print.scale?.[0] || 1) / fabricImage.width * flScaleX const scaleX = flWidth * (item.scale?.[0] || 1) / fabricImage.width * flScaleX
const scaleY = flHeight * (print.scale?.[1] || 1) / fabricImage.height * flScaleY const scaleY = flHeight * (item.scale?.[1] || 1) / fabricImage.height * flScaleY
const {x, y} = calculateRotatedTopLeftDeg( const {x, y} = calculateRotatedTopLeftDeg(
fabricImage.width * scaleX, fabricImage.width * scaleX,
fabricImage.height * scaleY, fabricImage.height * scaleY,
left, left,
top, top,
0, 0,
print.angle || 0 item.angle || 0
) )
const angle = print.angle || 0 const angle = item.angle || 0
fabricImage.set({ fabricImage.set({
left: x, left: x,
top: y, top: y,
@@ -1428,6 +1525,7 @@ export class CanvasManager {
selectable: true, selectable: true,
hasControls: true, hasControls: true,
hasBorders: true, hasBorders: true,
sourceData: item,
}); });
resolve(fabricImage); resolve(fabricImage);
}, { crossOrigin: "anonymous" }); }, { crossOrigin: "anonymous" });
@@ -1446,11 +1544,11 @@ export class CanvasManager {
}; };
// 添加平铺图层 // 添加平铺图层
for(let index = 0; index < singleLayers.length; index++){ for(let index = 0; index < singleLayers.length; index++){
let print = singleLayers[index]; let item = singleLayers[index];
let id = generateId("layer_image_"); let id = generateId("layer_image_");
let name = print.name; let name = item.name;
let image = await new Promise(resolve => { let image = await new Promise(resolve => {
fabric.Image.fromURL(print.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;
@@ -1461,12 +1559,11 @@ export class CanvasManager {
resolve(tcanvas); resolve(tcanvas);
}, { crossOrigin: "anonymous" }); }, { crossOrigin: "anonymous" });
}) })
console.log("==========添加平铺图层", fixedLayerObj.width,image.width) let scaleX = fixedLayerObj.width / image.width * (item.scale?.[0] || 1) / 5;
let scaleX = fixedLayerObj.width / image.width * (print.scale?.[0] || 1) / 5; let scaleY = fixedLayerObj.height / image.height * (item.scale?.[1] || 1) / 5;
let scaleY = fixedLayerObj.height / image.height * (print.scale?.[1] || 1) / 5;
let scale = fixedLayerObj.width > fixedLayerObj.height ? scaleX : scaleY; let scale = fixedLayerObj.width > fixedLayerObj.height ? scaleX : scaleY;
let left = (print.location?.[0] || 0) - image.width * scale / 2 let left = (item.location?.[0] || 0) - image.width * scale / 2
let top = (print.location?.[1] || 0) - image.height * scale / 2 let top = (item.location?.[1] || 0) - image.height * scale / 2
let rect = new fabric.Rect({ let rect = new fabric.Rect({
id: id, id: id,
layerId: id, layerId: id,
@@ -1479,13 +1576,21 @@ export class CanvasManager {
scaleY: fixedLayerObj.scaleY, scaleY: fixedLayerObj.scaleY,
originX: fixedLayerObj.originX, originX: fixedLayerObj.originX,
originY: fixedLayerObj.originY, originY: fixedLayerObj.originY,
sourceData: item,
fill: new fabric.Pattern({ fill: new fabric.Pattern({
source: image, source: image,
repeat: "repeat", repeat: "repeat",
patternTransform: createPatternTransform(scale, print.angle || 0), patternTransform: createPatternTransform(scale, item.angle || 0),
offsetX: left, // 水平偏移 offsetX: left, // 水平偏移
offsetY: top, // 垂直偏移 offsetY: top, // 垂直偏移
}), }),
fill_ : {
source: item.path,
gapX: 0,
gapY: 0,
width: image.width,
height: image.height,
}
}); });
this.canvas.add(rect); this.canvas.add(rect);
let layer = createLayer({ let layer = createLayer({
@@ -1531,31 +1636,32 @@ export class CanvasManager {
} }
/** /**
* * 画布事件变更后
*/ */
async changeCanvas(){ async changeCanvas(){
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); // const ids = this.layerManager.getBlendModeLayerIds(SpecialLayerId.SPECIAL_GROUP);
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);
return; // this.canvas.renderAll();
} // return;
const base64 = await this.exportManager.exportImage({layerIdArray2: ids, isEnhanceImg: true}); // }
if(!base64) return console.warn("导出图片失败", base64) // const base64 = await this.exportManager.exportImage({layerIdArray2: ids, isEnhanceImg: true});
const canvas = await base64ToCanvas(base64, fixedLayerObj.scaleX * 2, true); // if(!base64) return console.warn("导出图片失败", base64)
const ctx = canvas.getContext('2d'); // const canvas = await base64ToCanvas(base64, fixedLayerObj.scaleX * 2, true);
const width = fixedLayerObj.width; // const ctx = canvas.getContext('2d');
const height = fixedLayerObj.height; // const width = fixedLayerObj.width;
const x = (canvas.width - width) / 2; // const height = fixedLayerObj.height;
const y = (canvas.height - height) / 2; // const x = (canvas.width - width) / 2;
const data = ctx.getImageData(x, y, width, height); // const y = (canvas.height - height) / 2;
await this.setObjecCliptInfo(colorObject, data); // const data = ctx.getImageData(x, y, width, height);
this.canvas.renderAll(); // await this.setObjecCliptInfo(colorObject, data);
} // this.canvas.renderAll();
// }
} }
/** /**

View File

@@ -1,7 +1,7 @@
import { fabric } from "fabric-with-all"; import { fabric } from "fabric-with-all";
import { findObjectById } from "../utils/helper"; import { findObjectById } from "../utils/helper";
import { createRasterizedImage } from "../utils/selectionToImage"; import { createRasterizedImage } from "../utils/selectionToImage";
import { OperationType, SpecialLayerId } from "../utils/layerHelper"; import { OperationType, SpecialLayerId } from "../utils/layerHelper";
/** /**
* 图片导出管理器 * 图片导出管理器
@@ -26,7 +26,8 @@ export class ExportManager {
* @param {Array} options.layerIdArray2 导出多个图层ID数组2 * @param {Array} options.layerIdArray2 导出多个图层ID数组2
* @param {String} options.expPicType 导出图片类型 (png/jpg/svg) * @param {String} options.expPicType 导出图片类型 (png/jpg/svg)
* @param {Boolean} options.restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1 * @param {Boolean} options.restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1
* @param {Boolean} options.isEnhanceImg 是否是增强图片 * @param {Boolean} options.isEnhanceImg 是否是增强图片
* @param {Array} options.excludedLayers 排除的图层ID数组
* @returns {String} 导出的图片数据URL * @returns {String} 导出的图片数据URL
*/ */
async exportImage(options = {}) { async exportImage(options = {}) {
@@ -41,6 +42,7 @@ export class ExportManager {
expPicType = "png", expPicType = "png",
restoreOpacityInRedGreen = true, restoreOpacityInRedGreen = true,
isEnhanceImg, // 是否是增强图片 isEnhanceImg, // 是否是增强图片
excludedLayers = [], // 排除的图层ID数组
} = options; } = options;
try { try {
// 查找颜色图层并隐藏 // 查找颜色图层并隐藏
@@ -90,6 +92,7 @@ export class ExportManager {
isCropByBg, isCropByBg,
isEnhanceImg, // 是否是增强图片 isEnhanceImg, // 是否是增强图片
layerIdArray2, layerIdArray2,
excludedLayers, // 排除的图层ID数组
); );
} catch (error) { } catch (error) {
console.error("导出图片失败:", error); console.error("导出图片失败:", error);
@@ -240,7 +243,8 @@ export class ExportManager {
restoreOpacityInRedGreen, restoreOpacityInRedGreen,
isCropByBg, // 是否使用背景大小裁剪 isCropByBg, // 是否使用背景大小裁剪
isEnhanceImg, // 是否是增强图片 isEnhanceImg, // 是否是增强图片
layerIdArray, layerIdArray, // 导出所有图层
excludedLayers, // 排除的图层ID数组
) { ) {
// 按图层顺序收集对象(从底到顶) // 按图层顺序收集对象(从底到顶)
const objectsToExport = this._collectObjectsByLayerOrder( const objectsToExport = this._collectObjectsByLayerOrder(
@@ -248,6 +252,7 @@ export class ExportManager {
isContainBg, isContainBg,
isContainFixed, isContainFixed,
isContainFixedOther, // 是否包含其他固定图层 isContainFixedOther, // 是否包含其他固定图层
excludedLayers,
); );
if (objectsToExport.length === 0) { if (objectsToExport.length === 0) {
@@ -303,10 +308,11 @@ export class ExportManager {
/** /**
* 从图层收集对象(优化版本 - 通过ID查找画布中的真实对象 * 从图层收集对象(优化版本 - 通过ID查找画布中的真实对象
* @param {Object} layer 图层对象 * @param {Object} layer 图层对象
* @param {Boolean} isChildren 是否递归收集子图层的对象
* @returns {Array} 画布中的真实对象数组 * @returns {Array} 画布中的真实对象数组
* @private * @private
*/ */
_collectObjectsFromLayer(layer) { _collectObjectsFromLayer(layer, isChildren = true) {
if (!layer) { if (!layer) {
return []; return [];
} }
@@ -335,10 +341,10 @@ export class ExportManager {
} }
// 递归收集子图层的对象 // 递归收集子图层的对象
if (layer.children && layer.children.length > 0) { if (isChildren && layer.children && layer.children.length > 0) {
for (let i = layer.children.length - 1; i >= 0; i--) { for (let i = layer.children.length - 1; i >= 0; i--) {
const childLayer = layer.children[i]; const childLayer = layer.children[i];
const childObjects = this._collectObjectsFromLayer(childLayer); const childObjects = this._collectObjectsFromLayer(childLayer, isChildren);
realObjects.push(...childObjects); realObjects.push(...childObjects);
} }
} }
@@ -405,12 +411,13 @@ export class ExportManager {
* @param {Boolean} isContainBg 是否包含背景图层 * @param {Boolean} isContainBg 是否包含背景图层
* @param {Boolean} isContainFixed 是否包含固定图层 * @param {Boolean} isContainFixed 是否包含固定图层
* @param {Boolean} isContainFixedOther 是否包含其他固定图层 * @param {Boolean} isContainFixedOther 是否包含其他固定图层
* @param {Array} excludedLayers 排除的图层ID数组
* @returns {Array} 按正确顺序排列的真实对象数组 * @returns {Array} 按正确顺序排列的真实对象数组
* @private * @private
*/ */
_collectObjectsByLayerOrder(layerIdArray, isContainBg, isContainFixed, isContainFixedOther) { _collectObjectsByLayerOrder(layerIdArray, isContainBg, isContainFixed, isContainFixedOther, excludedLayers) {
const objectsToExport = []; const objectsToExport = [];
const allLayers = this._getAllLayersFlattened(); // 获取扁平化的图层列表 const allLayers = this._getAllLayersFlattened(excludedLayers); // 获取扁平化的图层列表
// 图层数组是从顶到底的顺序,需要反向遍历以获得从底到顶的渲染顺序 // 图层数组是从顶到底的顺序,需要反向遍历以获得从底到顶的渲染顺序
for (let i = allLayers.length - 1; i >= 0; i--) { for (let i = allLayers.length - 1; i >= 0; i--) {
@@ -424,7 +431,7 @@ export class ExportManager {
continue; continue;
if (layer.visible) { if (layer.visible) {
const layerObjects = this._collectObjectsFromLayer(layer); const layerObjects = this._collectObjectsFromLayer(layer, false);
objectsToExport.push(...layerObjects); objectsToExport.push(...layerObjects);
} }
} }
@@ -433,15 +440,19 @@ export class ExportManager {
} }
/** /**
* 获取扁平化的图层列表(包含子图层) * 获取扁平化的图层列表(包含子图层),排除指定的图层
* @param {Array} excludedLayers 排除的图层ID数组
* @returns {Array} 扁平化的图层数组 * @returns {Array} 扁平化的图层数组
* @private * @private
*/ */
_getAllLayersFlattened() { _getAllLayersFlattened(excludedLayers) {
const flattenedLayers = []; const flattenedLayers = [];
const rootLayers = this._getAllLayers(); const rootLayers = this._getAllLayers();
const flattenLayer = (layer) => { const flattenLayer = (layer) => {
// 检查是否在排除列表中
if (excludedLayers && excludedLayers.includes(layer.id)) return;
flattenedLayers.push(layer); flattenedLayers.push(layer);
// 递归处理子图层 // 递归处理子图层
@@ -456,7 +467,6 @@ export class ExportManager {
for (const layer of rootLayers) { for (const layer of rootLayers) {
flattenLayer(layer); flattenLayer(layer);
} }
return flattenedLayers; return flattenedLayers;
} }

View File

@@ -524,15 +524,16 @@ export class LayerManager {
* @param {string} name 图层名称 * @param {string} name 图层名称
* @param {string} type 图层类型 * @param {string} type 图层类型
* @param {Object} options 额外选项 * @param {Object} options 额外选项
* @param {boolean} isCmd 是否创建命令
* @returns {string} 新创建的图层ID * @returns {string} 新创建的图层ID
*/ */
async createLayer(name = null, type = LayerType.EMPTY, options = {}) { async createLayer(name = null, type = LayerType.EMPTY, options = {}, isCmd = true) {
// 生成唯一ID // 生成唯一ID
const layerId = options.id || options.layerId || generateId("layer_"); const layerId = options.id || options.layerId || generateId("layer_");
// 计算普通图层数量(非背景、非固定) // 计算普通图层数量(非背景、非固定)
const normalLayersCount = this.layers.value.filter( const normalLayersCount = this.layers.value.filter(
(layer) => !layer.isBackground && !layer.isFixed (layer) => !layer.isBackground && !layer.isFixed && !layer.isFixedOther
).length; ).length;
// 计算插入位置如果没有指定insertIndex则根据当前选中图层决定插入位置 // 计算插入位置如果没有指定insertIndex则根据当前选中图层决定插入位置
// 添加到图层列表 // 添加到图层列表
@@ -544,7 +545,7 @@ export class LayerManager {
// 创建新图层 // 创建新图层
const newLayer = createLayer({ const newLayer = createLayer({
id: layerId, id: layerId,
name: name || `图层 ${normalLayersCount + 1}`, name: name || this.t("Canvas.EmptyLayer"),
type: type, type: type,
visible: true, visible: true,
locked: false, locked: false,
@@ -573,13 +574,13 @@ export class LayerManager {
} }
// 执行命令 // 执行命令
if (this.commandManager) { if (isCmd && this.commandManager) {
await this.commandManager.execute(command); await this.commandManager.execute(command);
} else { } else{
await command.execute(); await command.execute();
} }
return layerId; return isCmd ? layerId : command;
} }
/** /**
@@ -973,7 +974,7 @@ export class LayerManager {
}) })
// const normalLayers = this.layers.value.filter((l) => !l.isBackground && !l.isFixed && !l.isFixedOther); // const normalLayers = this.layers.value.filter((l) => !l.isBackground && !l.isFixed && !l.isFixedOther);
console.log("普通图层:", normalLayers) console.log("普通图层:", normalLayers)
if (isChild ? parentLength <= 1 : normalLayers.length <= 1) { if (isChild ? parentLength <= 1 : false) {//normalLayers.length <= 1
console.warn("不能删除唯一的普通图层"); console.warn("不能删除唯一的普通图层");
message.warning(this.t("Canvas.cannotDeleteOnlyLayer")); message.warning(this.t("Canvas.cannotDeleteOnlyLayer"));
return false; return false;
@@ -3446,7 +3447,7 @@ export class LayerManager {
this.layers.value.forEach(layer => { this.layers.value.forEach(layer => {
if(layer.id === SpecialLayerId.SPECIAL_GROUP){ if(layer.id === SpecialLayerId.SPECIAL_GROUP){
layer.children.forEach(child => { layer.children.forEach(child => {
if(child.blendMode && child.blendMode !== BlendMode.NORMAL){ if(child.visible && child.blendMode && child.blendMode !== BlendMode.NORMAL){
blendModeLayerIds.push(child.id); blendModeLayerIds.push(child.id);
} }
}); });

View File

@@ -919,6 +919,26 @@ export function calculateRotatedTopLeftDeg(
return { x: newX, y: newY }; return { x: newX, y: newY };
} }
/**
* 根据左上角坐标计算中心点坐标
* @param {number} W - 宽度
* @param {number} H - 高度
* @param {number} currentX - 当前左上角x坐标
* @param {number} currentY - 当前左上角y坐标
* @param {number} currentAngleDeg - 当前角度(度)
* @returns {Object} 中心点坐标 {x, y}
*/
export function calculateCenterPoint(W, H, currentX, currentY, currentAngleDeg) {
const currentAngle = (currentAngleDeg * Math.PI) / 180;
const cosCurrent = Math.cos(currentAngle);
const sinCurrent = Math.sin(currentAngle);
const Cx = currentX + (W / 2) * cosCurrent - (H / 2) * sinCurrent;
const Cy = currentY + (W / 2) * sinCurrent + (H / 2) * cosCurrent;
return { x: Cx, y: Cy };
}
/** /**
* 创建缩放+旋转的变换矩阵 * 创建缩放+旋转的变换矩阵
* @param {number} scale - 缩放比例 * @param {number} scale - 缩放比例

View File

@@ -211,6 +211,7 @@ export function simplifyLayers(layers) {
fill: layer?.fill || null, fill: layer?.fill || null,
fillColor: layer.fillColor, fillColor: layer.fillColor,
selectObject: layer.selectObject, selectObject: layer.selectObject,
blendMode: layer.blendMode || null,
}; };
return simplifiedLayer; return simplifiedLayer;

View File

@@ -1,6 +1,6 @@
// 栅格化帮助 // 栅格化帮助
import { fabric } from "fabric-with-all"; import { fabric } from "fabric-with-all";
import { SpecialLayerId } from "./layerHelper";
/** /**
* 创建栅格化图像 - 重构版本 * 创建栅格化图像 - 重构版本
* 采用复制原对象+裁剪路径的方式,保持原始质量和准确位置 * 采用复制原对象+裁剪路径的方式,保持原始质量和准确位置
@@ -691,6 +691,16 @@ const cloneObjectAsync = (obj) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
obj.clone((cloned) => { obj.clone((cloned) => {
if (cloned) { if (cloned) {
cloned.set({
scaleX: obj.scaleX,
scaleY: obj.scaleY,
top: obj.top,
left: obj.left,
width: obj.width,
height: obj.height,
zoomX: obj.zoomX,
zoomY: obj.zoomY,
})
resolve(cloned); resolve(cloned);
} else { } else {
reject(new Error("对象克隆失败")); reject(new Error("对象克隆失败"));
@@ -845,9 +855,8 @@ const renderContentToImage = async ({
}); });
// 克隆并添加所有需要渲染的对象 // 克隆并添加所有需要渲染的对象
for (const obj of fabricObjects) { for (let obj of fabricObjects) {
const clonedObj = await cloneObjectAsync(obj); let clonedObj = await cloneObjectAsync(obj);
// 调整对象位置:将选区左上角作为新的原点(0,0) // 调整对象位置:将选区左上角作为新的原点(0,0)
clonedObj.set({ clonedObj.set({
left: (clonedObj.left - selectionBounds.left) * qualityMultiplier, left: (clonedObj.left - selectionBounds.left) * qualityMultiplier,
@@ -859,19 +868,19 @@ const renderContentToImage = async ({
}); });
// 如果有裁剪路径,也需要调整裁剪路径 // 如果有裁剪路径,也需要调整裁剪路径
if (clonedObj.clipPath) { if (clonedObj.clipPath && obj.id !== SpecialLayerId.COLOR) {
clonedObj.clipPath.set({ clonedObj.clipPath.set({
left: left: (clonedObj.clipPath.left - selectionBounds.left) * qualityMultiplier,
(clonedObj.clipPath.left - selectionBounds.left) * top: (clonedObj.clipPath.top - selectionBounds.top) * qualityMultiplier,
qualityMultiplier,
top:
(clonedObj.clipPath.top - selectionBounds.top) * qualityMultiplier,
scaleX: (clonedObj.clipPath.scaleX || 1) * qualityMultiplier, scaleX: (clonedObj.clipPath.scaleX || 1) * qualityMultiplier,
scaleY: (clonedObj.clipPath.scaleY || 1) * qualityMultiplier, scaleY: (clonedObj.clipPath.scaleY || 1) * qualityMultiplier,
}); });
clonedObj.clipPath.setCoords(); // 更新裁剪路径坐标 clonedObj.clipPath.setCoords(); // 更新裁剪路径坐标
} }
// if(obj.globalCompositeOperation === "multiply"){
// clonedObj.clipPath = null;
// }
console.log("==========", obj.id, obj.layerName);
contentCanvas.add(clonedObj); contentCanvas.add(clonedObj);
} }

View File

@@ -72,7 +72,9 @@ const exportImage = async () => {
if (canvasEditor.value) { if (canvasEditor.value) {
const base64 = await canvasEditor.value.exportImage({ const base64 = await canvasEditor.value.exportImage({
isContainFixed: false, // 是否导出底图 isContainFixed: false, // 是否导出底图
isContainFixedOther: false, // 是否导出其他固定图层
isContainBg: false, // 是否导出背景 isContainBg: false, // 是否导出背景
isEnhanceImg: false, // 是否导出增强图片
}); });
// 模拟下载图片 // 模拟下载图片
@@ -99,6 +101,16 @@ const exportColorLayer = async () => {
} }
}; };
// 导出所有信息
const exportAllInfo = async () => {
if (canvasEditor.value) {
const allInfo = await canvasEditor.value.exportAllInfo();
console.log("==========导出所有信息:", allInfo);
}
};
const changeCanvas = (command) => { const changeCanvas = (command) => {
console.log(command); console.log(command);
}; };
@@ -194,12 +206,19 @@ const frontBackChange =(value)=>{
// 自定义工具配置相关 // 自定义工具配置相关
const customToolsList = ref([ const customToolsList = ref([
{ {
id: "exportPNG", id: "exportColorLayer",
title: "导出颜色图层", title: "导出颜色图层",
action: exportColorLayer, action: exportColorLayer,
label: "导颜", label: "导颜",
class: "export-btn", class: "export-btn",
}, },
{
id: "exportAllInfo",
title: "导出所有信息",
action: exportAllInfo,
label: "导All",
class: "export-btn",
},
{ {
id: "exportPNG", id: "exportPNG",
title: "导出PNG", //导出画布图片 title: "导出PNG", //导出画布图片