Files
aida_front/src/component/Canvas/CanvasEditor/commands/FillRepeatCommand.js
2026-01-05 11:47:36 +08:00

302 lines
9.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Command } from "./Command";
import { findLayerRecursively } from "../utils/layerHelper";
import { fabric } from "fabric-with-all";
import {
findObjectById,
generateId,
insertObjectAtZIndex,
removeCanvasObjectByObject,
createPatternTransform,
} from "../utils/helper";
import { restoreFabricObject } from "../utils/objectHelper";
const scale = 0.3;// 默认缩放比例
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;
this.oldIsDisableUnlock = 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;
}
console.log("===========", object.toObject(["id", "layerId", "layerName"]))
this.oldObjects = object;
const img = await new Promise((resolve, reject) => {
if (object.type === "rect") {
let source = object.fill.source;
resolve(source);
} else if (object.type === "image") {
// resolve(object.getElement());
// fabric.Image.fromURL(
// object.src,
// v => resolve(v),
// { crossOrigin: "anonymous" }
// );
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);
}
});
const fill_ = {
source: FillSourceToBase64(img),
gapX: 0,
gapY: 0,
};
const bgObject = this.canvasManager.getBackgroundLayerObject();
const pattern = new fabric.Pattern({
source: img,
repeat: this.fillRepeat,
patternTransform: object.fill?.hasOwnProperty("patternTransform") ? object.fill.patternTransform : createPatternTransform(scale, 0),
offsetX: object.fill?.hasOwnProperty("offsetX") ? object.fill.offsetX : bgObject.width / 2, // 水平偏移
offsetY: object.fill?.hasOwnProperty("offsetY") ? object.fill.offsetY : bgObject.height / 2, // 垂直偏移
});
const rect = new fabric.Rect({
id: object.id,
layerId: object.layerId,
layerName: object.layerName,
fill_,
});
layer.fabricObjects = [rect.toObject(["id", "layerId", "layerName"])];
this.oldLocked = layer.locked;
// this.oldIsDisableUnlock = layer.isDisableUnlock;
// layer.isDisableUnlock = true;
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 {
rect.set({
width: bgObject.width,
height: bgObject.height,
top: bgObject.top,
left: bgObject.left,
originX: bgObject.originX,
originY: bgObject.originY,
});
layer.locked = true;
}
rect.set("fill", pattern);
this.canvas.add(rect);
this.canvas.remove(object);
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"])];
layer.locked = this.oldLocked;
// layer.isDisableUnlock = this.oldIsDisableUnlock;
await this.layerManager?.updateLayersObjectsInteractivity();
await this.layerManager?.sortLayersWithTool?.();
this.canvas.renderAll();
this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layerId);
return true;
}
}
/**
* 填充图案更改参数
*/
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);
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;
}
async execute(isUndo = 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;
}
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();
image.src = object.fill_.source;
await image.decode();
// 创建透明 Canvas
const tcanvas = document.createElement('canvas');
tcanvas.width = image.width + object.fill_.gapX;
tcanvas.height = image.height + object.fill_.gapY;
const ctx = tcanvas.getContext('2d');
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
ctx.drawImage(image, 0, 0);
const fill = object.get("fill");
fill.source = tcanvas;
object.set("fill", new fabric.Pattern(fill));
this.canvas.renderAll();
return true;
}
async undo() {
if (this.oldGapX === null || this.oldGapY === null) {
console.warn("没有旧间隙可恢复");
return false;
}
await this.execute(true);
return true;
}
}