Files
aida_front/src/component/Canvas/CanvasEditor/commands/FillRepeatCommand.js

346 lines
10 KiB
JavaScript
Raw Normal View History

2026-01-02 11:24:11 +08:00
import { Command } from "./Command";
import { findLayerRecursively } from "../utils/layerHelper";
import { fabric } from "fabric-with-all";
import {
findObjectById,
generateId,
insertObjectAtZIndex,
removeCanvasObjectByObject,
createPatternTransform,
2026-01-29 17:16:31 +08:00
getTransformScaleAngle,
2026-01-16 10:29:03 +08:00
imageAddGapToCanvas,
2026-01-02 11:24:11 +08:00
} from "../utils/helper";
import { restoreFabricObject } from "../utils/objectHelper";
export const FillSourceToBase64 = (source) => {
if (source?.toDataURL) {
return source.toDataURL?.();
} else if (source?.src) {
return source.src;
}
return source;
}
/**
* 填充图案平铺命令
* 填充重复属性repeat | repeat-x | repeat-y | no-repeat
* 默认缩放比例0.3
* 默认偏移量50%
*/
export class FillRepeatCommand extends Command {
constructor(options) {
super({ name: "填充图案平铺", saveState: true });
this.canvas = options.canvas;
this.layers = options.layers;
this.canvasManager = options.canvasManager;
this.layerManager = options.layerManager;
this.layerId = options.layerId;
this.fillRepeat = options.fillRepeat;
this.oldObjects = null;
this.oldLocked = null;
}
async execute() {
const { layer } = findLayerRecursively(this.layers.value, this.layerId);
if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) {
console.warn("图层不存在或没有 fabric 对象");
return false;
}
const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id);
if (!object || (object.type !== "rect" && object.type !== "image")) {
console.warn("当前对象不能平铺", object.type);
return false;
}
this.oldObjects = object;
2026-01-08 15:25:15 +08:00
if (this.fillRepeat === "no-repeat") {
const fill_ = object.fill_;
const image = await new Promise((resolve, reject) => {
fabric.Image.fromURL(
fill_.source,
v => resolve(v),
{ crossOrigin: "anonymous" }
);
});
image.set({
2026-01-29 17:16:31 +08:00
...this.copyObjectProperties(object),
2026-01-08 15:25:15 +08:00
...(fill_.originalInfo || {
top: object.top,
left: object.left,
})
2026-01-02 11:24:11 +08:00
});
2026-01-08 15:25:15 +08:00
layer.fabricObjects = [image.toObject(["id", "layerId", "layerName"])];
2026-01-29 17:16:31 +08:00
// this.oldLocked = layer.locked;
// layer.locked = false;
2026-01-08 15:25:15 +08:00
this.canvas.add(image);
this.canvas.remove(object);
2026-01-02 11:24:11 +08:00
} else {
2026-01-08 15:25:15 +08:00
const img = await new Promise((resolve, reject) => {
if (object.type === "rect") {
let source = object.fill.source;
resolve(source);
} else if (object.type === "image") {
const imgElement = object.getElement();
// 创建透明 Canvas
const tcanvas = document.createElement('canvas');
tcanvas.width = imgElement.width;
tcanvas.height = imgElement.height;
const ctx = tcanvas.getContext('2d');
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
ctx.drawImage(imgElement, 0, 0);
resolve(tcanvas);
}
2026-01-02 11:24:11 +08:00
});
2026-01-08 15:25:15 +08:00
const fill_ = object.fill_ || {
source: FillSourceToBase64(img),
gapX: 0,
gapY: 0,
width: img.width,
height: img.height,
originalInfo: {
top: object.top,
left: object.left,
scaleX: object.scaleX,
scaleY: object.scaleY,
width: object.width,
height: object.height,
}
};
2026-01-19 13:37:24 +08:00
const fdObject = this.canvasManager.getFixedLayerObject();
2026-01-08 15:25:15 +08:00
const bgObject = this.canvasManager.getBackgroundLayerObject();
2026-01-19 13:37:24 +08:00
const tObject = fdObject || bgObject;
2026-01-29 17:16:31 +08:00
// const offsetX = object.fill?.hasOwnProperty("offsetX") ? object.fill.offsetX : tObject.width / 2;
// const offsetY = object.fill?.hasOwnProperty("offsetY") ? object.fill.offsetY : tObject.height / 2;
const patternTransform = object.fill?.hasOwnProperty("patternTransform") ? object.fill.patternTransform : createPatternTransform(0.3, 0);
const scale = getTransformScaleAngle(patternTransform).scale;
const offsetX = tObject.width / 2 - img.width * scale / 2;
const offsetY = tObject.height / 2 - img.height * scale / 2;
2026-01-08 15:25:15 +08:00
const pattern = new fabric.Pattern({
source: img,
repeat: this.fillRepeat,
2026-01-29 17:16:31 +08:00
patternTransform,
offsetX, // 水平偏移
offsetY, // 垂直偏移
2026-01-08 15:25:15 +08:00
});
const rect = new fabric.Rect({
2026-01-29 17:16:31 +08:00
...this.copyObjectProperties(object),
2026-01-08 15:25:15 +08:00
fill_,
});
layer.fabricObjects = [rect.toObject(["id", "layerId", "layerName"])];
2026-01-29 17:16:31 +08:00
// this.oldLocked = layer.locked;
2026-01-08 15:25:15 +08:00
if (this.oldObjects.type === "rect") {
rect.set({
width: object.width,
height: object.height,
top: object.top,
left: object.left,
originX: object.originX,
originY: object.originY,
angle: object.angle,
scaleX: object.scaleX,
scaleY: object.scaleY,
flipX: object.flipX,
flipY: object.flipY,
});
} else {
2026-01-19 13:37:24 +08:00
let scaleX = tObject.scaleX || 1;
let scaleY = tObject.scaleY || 1;
2026-01-08 15:25:15 +08:00
rect.set({
2026-01-19 13:37:24 +08:00
width: tObject.width,
height: tObject.height,
top: tObject.top - tObject.height * scaleY / 2,
left: tObject.left - tObject.width * scaleX / 2,
2026-01-08 15:25:15 +08:00
scaleX,
scaleY,
});
2026-01-29 17:16:31 +08:00
// layer.locked = true;
2026-01-08 15:25:15 +08:00
}
rect.set("fill", pattern);
this.canvas.add(rect);
this.canvas.remove(object);
2026-01-02 11:24:11 +08:00
}
await this.layerManager?.updateLayersObjectsInteractivity();
await this.layerManager?.sortLayersWithTool?.();
await this.canvasManager.thumbnailManager?.generateLayerThumbnail(
this.layerId
);
await this.layerManager.selectLayerObjects(this.layerId);
return true;
}
async undo() {
if (!this.oldObjects) {
console.warn("没有旧对象可恢复");
return false;
}
const { layer } = findLayerRecursively(this.layers.value, this.oldObjects.layerId);
if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) {
console.warn("图层不存在或没有 fabric 对象");
return false;
}
const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id);
this.canvas.remove(object);
this.canvas.add(this.oldObjects);
layer.fabricObjects = [this.oldObjects.toObject(["id", "layerId", "layerName"])];
2026-01-29 17:16:31 +08:00
// layer.locked = this.oldLocked;
2026-01-02 11:24:11 +08:00
await this.layerManager?.updateLayersObjectsInteractivity();
await this.layerManager?.sortLayersWithTool?.();
this.canvas.renderAll();
this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layerId);
return true;
}
2026-01-29 17:16:31 +08:00
// 复制原对象的属性
copyObjectProperties(object) {
return{
id: object.id,
layerId: object.layerId,
layerName: object.layerName,
isPrintTrims: object.isPrintTrims,
}
}
2026-01-02 11:24:11 +08:00
}
/**
* 填充图案更改参数
*/
export class FillRepeatChangeCommand extends Command {
constructor(options) {
super({ name: "填充图案更改参数", saveState: true });
this.canvas = options.canvas;
this.layers = options.layers;
this.canvasManager = options.canvasManager;
this.layerManager = options.layerManager;
this.layerId = options.layerId;
this.newPattern = options.newPattern;
this.oldPattern = null;
}
async execute() {
const { layer } = findLayerRecursively(this.layers.value, this.layerId);
if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) {
console.warn("图层不存在或没有 fabric 对象");
return false;
}
const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id);
if (!object || object.type !== "rect") {
console.warn("当前对象不是矩形", object);
return false;
}
this.oldPattern = object.oldPattern || object.get("fill");
delete object.oldPattern;
const oldPattern = { ...this.oldPattern };
delete oldPattern.id;
const pattern = new fabric.Pattern({
...oldPattern,
...this.newPattern,
});
object.set("fill", pattern);
2026-01-29 17:16:31 +08:00
if (object.globalCompositeOperation_) {
object.globalCompositeOperation = object.globalCompositeOperation_;
object.globalCompositeOperation_ = null;
}
2026-01-02 11:24:11 +08:00
this.canvas.renderAll();
return true;
}
async undo() {
if (!this.oldPattern) {
console.warn("没有旧图案可恢复");
return false;
}
const { layer } = findLayerRecursively(this.layers.value, this.layerId);
if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) {
console.warn("图层不存在或没有 fabric 对象");
return false;
}
const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id);
if (!object || object.type !== "rect") {
console.warn("当前对象不是矩形", object);
return false;
}
const pattern = new fabric.Pattern({
...this.oldPattern
});
object.set("fill", pattern);
this.canvas.renderAll();
return true;
}
}
/**
* 填充图案更改间隙
*/
export class FillRepeatGapChangeCommand extends Command {
constructor(options) {
super({ name: "填充图案更改间隙", saveState: true });
this.canvas = options.canvas;
this.layers = options.layers;
this.canvasManager = options.canvasManager;
this.layerManager = options.layerManager;
this.layerId = options.layerId;
this.newGapX = options.newGapX;
this.newGapY = options.newGapY;
this.record = !!options.record;
this.oldGapX = null;
this.oldGapY = null;
}
2026-01-29 17:16:31 +08:00
async execute(isCommand = true, isUndo = false) {
2026-01-02 11:24:11 +08:00
const { layer } = findLayerRecursively(this.layers.value, this.layerId);
if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) {
console.warn("图层不存在或没有 fabric 对象");
return false;
}
const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id);
if (!object || object.type !== "rect") {
console.warn("当前对象不是矩形", object);
return false;
}
if (!object.fill_) {
object.fill_ = {
source: FillSourceToBase64(object.fill.source),
gapX: 0,
gapY: 0,
}
}
if (isUndo) {
object.fill_.gapX = this.oldGapX;
object.fill_.gapY = this.oldGapY;
} else {
if (!object.oldFill_ && this.record) {
object.oldFill_ = { ...object.fill_ };
}
this.oldGapX = object.fill_.gapX;
this.oldGapY = object.fill_.gapY;
object.fill_.gapX = this.newGapX;
object.fill_.gapY = this.newGapY;
}
const image = new Image();
2026-01-19 13:37:24 +08:00
image.crossOrigin = "anonymous";
2026-01-02 11:24:11 +08:00
image.src = object.fill_.source;
await image.decode();
2026-01-06 14:17:04 +08:00
object.fill_.width = image.width;
object.fill_.height = image.height;
2026-01-02 11:24:11 +08:00
const fill = object.get("fill");
2026-01-16 10:29:03 +08:00
fill.source = imageAddGapToCanvas(image, object.fill_.gapX, object.fill_.gapY);
2026-01-02 11:24:11 +08:00
object.set("fill", new fabric.Pattern(fill));
2026-01-29 17:16:31 +08:00
if (isCommand && object.globalCompositeOperation_) {
object.globalCompositeOperation = object.globalCompositeOperation_;
object.globalCompositeOperation_ = null;
}
2026-01-02 11:24:11 +08:00
this.canvas.renderAll();
return true;
}
async undo() {
if (this.oldGapX === null || this.oldGapY === null) {
console.warn("没有旧间隙可恢复");
return false;
}
2026-01-29 17:16:31 +08:00
await this.execute(true, true);
2026-01-02 11:24:11 +08:00
return true;
}
}