画布印花合成
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
createLayer,
|
||||
LayerType,
|
||||
SpecialLayerId,
|
||||
BlendMode,
|
||||
} from "../utils/layerHelper";
|
||||
import { ObjectMoveCommand } from "../commands/ObjectCommands";
|
||||
import { AnimationManager } from "./animation/AnimationManager";
|
||||
@@ -30,6 +31,7 @@ import {
|
||||
fillToCssStyle,
|
||||
calculateRotatedTopLeftDeg,
|
||||
createPatternTransform,
|
||||
base64ToCanvas,
|
||||
} from "../utils/helper";
|
||||
import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands";
|
||||
import { isFunction } from "lodash-es";
|
||||
@@ -564,15 +566,14 @@ export class CanvasManager {
|
||||
}
|
||||
|
||||
// 更新颜色层信息
|
||||
const fixedLayerObj = this.getFixedLayerObject();
|
||||
const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
|
||||
if(colorObject && fixedLayerObj){
|
||||
await this.setColorObjectInfo(colorObject, fixedLayerObj);
|
||||
}
|
||||
// const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
|
||||
// if(colorObject){
|
||||
// await this.setObjecCliptInfo(colorObject);
|
||||
// }
|
||||
const groupLayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
|
||||
if(groupLayer && fixedLayerObj){
|
||||
if(groupLayer){
|
||||
const groupRect = new fabric.Rect({});
|
||||
await this.setColorObjectInfo(groupRect, fixedLayerObj);
|
||||
await this.setObjecCliptInfo(groupRect);
|
||||
groupLayer.clippingMask = groupRect.toObject();
|
||||
}
|
||||
|
||||
@@ -908,6 +909,7 @@ export class CanvasManager {
|
||||
* @param {Object} options 导出选项
|
||||
* @param {Boolean} options.isContainBg 是否包含背景图层
|
||||
* @param {Boolean} options.isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} options.isContainFixedOther 是否包含其他固定图层
|
||||
* @param {String} options.layerId 导出具体图层ID
|
||||
* @param {Array} options.layerIdArray 导出多个图层ID数组
|
||||
* @param {String} options.expPicType 导出图片类型 (png/jpg/svg)
|
||||
@@ -948,7 +950,7 @@ export class CanvasManager {
|
||||
const normalLayerIds =
|
||||
this.layers?.value
|
||||
?.filter(
|
||||
(layer) => !layer.isBackground && !layer.isFixed && layer.visible
|
||||
(layer) => !layer.isBackground && !layer.isFixed && !layer.isFixedOther && layer.visible
|
||||
)
|
||||
?.map((layer) => layer.id) || [];
|
||||
|
||||
@@ -1233,7 +1235,7 @@ export class CanvasManager {
|
||||
// console.log("图层关联验证结果:", isValidate);
|
||||
// 排序
|
||||
// 使用LayerSort工具重新排列画布对象(如果可用)
|
||||
await this?.layerManager?.layerSort?.rearrangeObjects();
|
||||
// await this?.layerManager?.layerSort?.rearrangeObjects();
|
||||
|
||||
this.layerManager.activeLayerId.value = this.layers.value[0]
|
||||
.children?.length
|
||||
@@ -1304,10 +1306,15 @@ export class CanvasManager {
|
||||
})
|
||||
await this.createPrintTrimsLayers(printTrimsLayers, singleLayers);
|
||||
}
|
||||
|
||||
await this.changeCanvas();
|
||||
}
|
||||
|
||||
async setColorObjectInfo(colorRect, fixedLayerObj){
|
||||
colorRect.set({
|
||||
// 设置画布对象的裁剪信息
|
||||
async setObjecCliptInfo(tagObject, data){
|
||||
const fixedLayerObj = this.getFixedLayerObject();
|
||||
if(!fixedLayerObj) return console.warn("固定图层为空");
|
||||
tagObject.set({
|
||||
top: fixedLayerObj.top,
|
||||
left: fixedLayerObj.left,
|
||||
width: fixedLayerObj.width,
|
||||
@@ -1322,7 +1329,7 @@ export class CanvasManager {
|
||||
if(imageUrl){
|
||||
object = await new Promise((resolve, reject) => {
|
||||
fabric.Image.fromURL(imageUrl, (imgObject) => {
|
||||
colorRect.set({
|
||||
tagObject.set({
|
||||
width: imgObject.width,
|
||||
height: imgObject.height,
|
||||
});
|
||||
@@ -1330,14 +1337,14 @@ export class CanvasManager {
|
||||
}, { crossOrigin: "anonymous" });
|
||||
});
|
||||
}
|
||||
const canvas = getObjectAlphaToCanvas(object);
|
||||
const canvas = getObjectAlphaToCanvas(object, data);
|
||||
const transparentMask = new fabric.Image(canvas, {
|
||||
top: 0,
|
||||
left: 0,
|
||||
originX: fixedLayerObj.originX,
|
||||
originY: fixedLayerObj.originY,
|
||||
});
|
||||
colorRect.set('clipPath', transparentMask);
|
||||
tagObject.set('clipPath', transparentMask);
|
||||
}
|
||||
async createColorLayer(color){
|
||||
if(!color) return console.warn("颜色为空不需要添加");
|
||||
@@ -1351,8 +1358,9 @@ export class CanvasManager {
|
||||
layerName: t("Canvas.color"),
|
||||
isVisible: true,
|
||||
isLocked: true,
|
||||
globalCompositeOperation: BlendMode.MULTIPLY,
|
||||
});
|
||||
await this.setColorObjectInfo(colorRect, fixedLayerObj);
|
||||
// await this.setObjecCliptInfo(colorRect);
|
||||
const gradientObj = palletToFill(color);
|
||||
const gradient = new fabric.Gradient({
|
||||
type: 'linear',
|
||||
@@ -1370,6 +1378,7 @@ export class CanvasManager {
|
||||
locked: colorRect.isLocked,
|
||||
opacity: 1.0,
|
||||
isFixedOther: true,
|
||||
blendMode: BlendMode.MULTIPLY,
|
||||
fabricObjects: [colorRect.toObject(["id", "layerId", "layerName"])],
|
||||
})
|
||||
const groupIndex = this.layers.value.findIndex(layer => layer.isFixed || layer.isBackground);
|
||||
@@ -1503,7 +1512,7 @@ export class CanvasManager {
|
||||
children.push(layer);
|
||||
}
|
||||
const groupRect = new fabric.Rect({});
|
||||
await this.setColorObjectInfo(groupRect, fixedLayerObj);
|
||||
await this.setObjecCliptInfo(groupRect);
|
||||
// 插入组图层
|
||||
const groupIndex = this.layers.value.findIndex(layer => layer.isFixedOther || layer.isFixed || layer.isBackground);
|
||||
const groupLayer = createLayer({
|
||||
@@ -1521,6 +1530,34 @@ export class CanvasManager {
|
||||
this.layers.value.splice(groupIndex, 0, groupLayer);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
async changeCanvas(){
|
||||
const fixedLayerObj = this.getFixedLayerObject();
|
||||
if(!fixedLayerObj) return console.warn("固定图层对象不存在", fixedLayerObj)
|
||||
const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
|
||||
if(colorObject){
|
||||
const ids = this.layerManager.getBlendModeLayerIds(SpecialLayerId.SPECIAL_GROUP);
|
||||
if(ids.length === 0){
|
||||
ids.unshift(SpecialLayerId.SPECIAL_GROUP);
|
||||
await this.setObjecCliptInfo(colorObject);
|
||||
return;
|
||||
}
|
||||
const base64 = await this.exportManager.exportImage({layerIdArray2: ids, isEnhanceImg: true});
|
||||
if(!base64) return console.warn("导出图片失败", base64)
|
||||
const canvas = await base64ToCanvas(base64, fixedLayerObj.scaleX * 2, true);
|
||||
const ctx = canvas.getContext('2d');
|
||||
const width = fixedLayerObj.width;
|
||||
const height = fixedLayerObj.height;
|
||||
const x = (canvas.width - width) / 2;
|
||||
const y = (canvas.height - height) / 2;
|
||||
const data = ctx.getImageData(x, y, width, height);
|
||||
await this.setObjecCliptInfo(colorObject, data);
|
||||
this.canvas.renderAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩放红绿图模式内容以适应当前画布大小
|
||||
* 确保衣服底图和红绿图永远在画布内可见
|
||||
|
||||
@@ -19,9 +19,11 @@ export class ExportManager {
|
||||
* @param {Object} options 导出选项
|
||||
* @param {Boolean} options.isContainBg 是否包含背景图层
|
||||
* @param {Boolean} options.isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} options.isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} options.isCropByBg 是否使用背景大小裁剪
|
||||
* @param {String} options.layerId 导出具体图层ID
|
||||
* @param {Array} options.layerIdArray 导出多个图层ID数组
|
||||
* @param {Array} options.layerIdArray2 导出多个图层ID数组2
|
||||
* @param {String} options.expPicType 导出图片类型 (png/jpg/svg)
|
||||
* @param {Boolean} options.restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1
|
||||
* @param {Boolean} options.isEnhanceImg 是否是增强图片
|
||||
@@ -31,20 +33,22 @@ export class ExportManager {
|
||||
const {
|
||||
isContainBg = false,
|
||||
isContainFixed = false,
|
||||
isContainFixedOther = false, // 是否包含其他固定图层
|
||||
isCropByBg = false, // 是否使用背景大小裁剪
|
||||
layerId = "",
|
||||
layerIdArray = [],
|
||||
layerIdArray2 = null,
|
||||
expPicType = "png",
|
||||
restoreOpacityInRedGreen = true,
|
||||
isEnhanceImg, // 是否是增强图片
|
||||
} = options;
|
||||
try {
|
||||
// 查找颜色图层并隐藏
|
||||
const colorLayer = this.layerManager.getLayerById(SpecialLayerId.COLOR);
|
||||
if (colorLayer && colorLayer.visible) {
|
||||
colorLayer.visible = false;
|
||||
await this.layerManager?.updateLayersObjectsInteractivity();
|
||||
}
|
||||
// const colorLayer = this.layerManager.getLayerById(SpecialLayerId.COLOR);
|
||||
// if (colorLayer && colorLayer.visible) {
|
||||
// colorLayer.visible = false;
|
||||
// await this.layerManager?.updateLayersObjectsInteractivity();
|
||||
// }
|
||||
|
||||
// 检查是否为红绿图模式
|
||||
const isRedGreenMode = this.layerManager?.isInRedGreenMode?.() || false;
|
||||
@@ -67,6 +71,7 @@ export class ExportManager {
|
||||
expPicType,
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isRedGreenMode,
|
||||
restoreOpacityInRedGreen,
|
||||
isCropByBg,
|
||||
@@ -79,10 +84,12 @@ export class ExportManager {
|
||||
expPicType,
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isRedGreenMode,
|
||||
restoreOpacityInRedGreen,
|
||||
isCropByBg,
|
||||
isEnhanceImg, // 是否是增强图片
|
||||
layerIdArray2,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("导出图片失败:", error);
|
||||
@@ -155,6 +162,7 @@ export class ExportManager {
|
||||
* @param {String} expPicType 导出类型
|
||||
* @param {Boolean} isContainBg 是否包含背景图层
|
||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} isRedGreenMode 是否为红绿图模式
|
||||
* @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1
|
||||
* @param {Boolean} isCropByBg 是否使用背景大小裁剪
|
||||
@@ -167,6 +175,7 @@ export class ExportManager {
|
||||
expPicType,
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isRedGreenMode,
|
||||
restoreOpacityInRedGreen,
|
||||
isCropByBg, // 是否使用背景大小裁剪
|
||||
@@ -180,7 +189,8 @@ export class ExportManager {
|
||||
const objectsToExport = this._collectObjectsByLayerOrder(
|
||||
layerIdArray,
|
||||
isContainBg,
|
||||
isContainFixed
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
);
|
||||
|
||||
if (objectsToExport.length === 0) {
|
||||
@@ -212,10 +222,12 @@ export class ExportManager {
|
||||
* @param {String} expPicType 导出类型
|
||||
* @param {Boolean} isContainBg 是否包含背景图层
|
||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} isRedGreenMode 是否为红绿图模式
|
||||
* @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1
|
||||
* @param {Boolean} isCropByBg 是否使用背景大小裁剪
|
||||
* @param {Boolean} isEnhanceImg 是否是增强图片
|
||||
* @param {Array} layerIdArray 导出多个图层ID数组2
|
||||
* @returns {String} 图片数据URL
|
||||
* @private
|
||||
*/
|
||||
@@ -223,16 +235,19 @@ export class ExportManager {
|
||||
expPicType,
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isRedGreenMode,
|
||||
restoreOpacityInRedGreen,
|
||||
isCropByBg, // 是否使用背景大小裁剪
|
||||
isEnhanceImg, // 是否是增强图片
|
||||
isEnhanceImg, // 是否是增强图片
|
||||
layerIdArray,
|
||||
) {
|
||||
// 按图层顺序收集对象(从底到顶)
|
||||
const objectsToExport = this._collectObjectsByLayerOrder(
|
||||
null, // 导出所有图层
|
||||
layerIdArray, // 导出所有图层
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
);
|
||||
|
||||
if (objectsToExport.length === 0) {
|
||||
@@ -389,10 +404,11 @@ export class ExportManager {
|
||||
* @param {Array|null} layerIdArray 图层ID数组,null表示所有图层
|
||||
* @param {Boolean} isContainBg 是否包含背景图层
|
||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||
* @returns {Array} 按正确顺序排列的真实对象数组
|
||||
* @private
|
||||
*/
|
||||
_collectObjectsByLayerOrder(layerIdArray, isContainBg, isContainFixed) {
|
||||
_collectObjectsByLayerOrder(layerIdArray, isContainBg, isContainFixed, isContainFixedOther) {
|
||||
const objectsToExport = [];
|
||||
const allLayers = this._getAllLayersFlattened(); // 获取扁平化的图层列表
|
||||
|
||||
@@ -404,7 +420,7 @@ export class ExportManager {
|
||||
if (layerIdArray && !layerIdArray.includes(layer.id)) continue;
|
||||
|
||||
// 检查图层类型过滤条件
|
||||
if (!this._shouldIncludeLayer(layer, isContainBg, isContainFixed))
|
||||
if (!this._shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther))
|
||||
continue;
|
||||
|
||||
if (layer.visible) {
|
||||
@@ -1019,10 +1035,11 @@ export class ExportManager {
|
||||
* @param {Object} layer 图层对象
|
||||
* @param {Boolean} isContainBg 是否包含背景图层
|
||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||
* @returns {Boolean} 是否应该包含
|
||||
* @private
|
||||
*/
|
||||
_shouldIncludeLayer(layer, isContainBg, isContainFixed) {
|
||||
_shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther) {
|
||||
if (!layer) return false;
|
||||
|
||||
// 检查背景图层
|
||||
@@ -1035,6 +1052,11 @@ export class ExportManager {
|
||||
return isContainFixed;
|
||||
}
|
||||
|
||||
// 检查其他固定图层
|
||||
if (layer.isFixedOther) {
|
||||
return isContainFixedOther;
|
||||
}
|
||||
|
||||
// 普通图层总是包含
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3436,4 +3436,22 @@ export class LayerManager {
|
||||
|
||||
console.log("🎨 已设置组遮罩移动同步 - 使用 object:modified 事件");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取印花和颜色图层设置了blendMode的图层ID
|
||||
* @returns {string[]} - 包含blendMode的图层ID数组
|
||||
*/
|
||||
getBlendModeLayerIds() {
|
||||
const blendModeLayerIds = [];
|
||||
this.layers.value.forEach(layer => {
|
||||
if(layer.id === SpecialLayerId.SPECIAL_GROUP){
|
||||
layer.children.forEach(child => {
|
||||
if(child.blendMode && child.blendMode !== BlendMode.NORMAL){
|
||||
blendModeLayerIds.push(child.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return blendModeLayerIds;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user