2025-07-10 01:01:46 +08:00
|
|
|
|
import { fabric } from "fabric-with-all";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 将序列化对象恢复为 fabric 对象
|
|
|
|
|
|
* @param {Object} serializedObject - toObject() 生成的对象
|
|
|
|
|
|
* @param {fabric.Canvas} canvas - 目标画布
|
|
|
|
|
|
* @returns {Promise<fabric.Object>} 恢复的 fabric 对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function restoreFabricObject(serializedObject, canvas) {
|
2026-01-02 11:24:11 +08:00
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
const objectType = serializedObject.type;
|
|
|
|
|
|
// 定义恢复后的处理函数
|
|
|
|
|
|
const handleRestoredObject = (fabricObject) => {
|
|
|
|
|
|
if (!fabricObject) {
|
|
|
|
|
|
reject(new Error(`无法恢复 ${objectType} 类型的对象`));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 恢复自定义属性
|
|
|
|
|
|
if (serializedObject.id) fabricObject.id = serializedObject.id;
|
|
|
|
|
|
if (serializedObject.layerId) fabricObject.layerId = serializedObject.layerId;
|
|
|
|
|
|
if (serializedObject.layerName) fabricObject.layerName = serializedObject.layerName;
|
2025-07-10 01:01:46 +08:00
|
|
|
|
|
2026-01-02 11:24:11 +08:00
|
|
|
|
// 更新坐标
|
|
|
|
|
|
fabricObject.setCoords();
|
2025-07-10 01:01:46 +08:00
|
|
|
|
|
2026-01-02 11:24:11 +08:00
|
|
|
|
// 添加到画布
|
|
|
|
|
|
// canvas.add(fabricObject);
|
2025-07-10 01:01:46 +08:00
|
|
|
|
|
2026-01-02 11:24:11 +08:00
|
|
|
|
resolve(fabricObject);
|
|
|
|
|
|
};
|
2025-07-10 01:01:46 +08:00
|
|
|
|
|
2026-01-02 11:24:11 +08:00
|
|
|
|
// 根据类型选择恢复方法
|
|
|
|
|
|
switch (objectType) {
|
|
|
|
|
|
case "rect":
|
|
|
|
|
|
fabric.Rect.fromObject(serializedObject, handleRestoredObject);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "circle":
|
|
|
|
|
|
fabric.Circle.fromObject(serializedObject, handleRestoredObject);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "path":
|
|
|
|
|
|
fabric.Path.fromObject(serializedObject, handleRestoredObject);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "image":
|
|
|
|
|
|
fabric.Image.fromObject(serializedObject, handleRestoredObject);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "group":
|
|
|
|
|
|
fabric.Group.fromObject(serializedObject, handleRestoredObject);
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
// 使用通用方法
|
|
|
|
|
|
fabric.util.enlivenObjects([serializedObject], (objects) => {
|
|
|
|
|
|
if (objects && objects[0]) {
|
|
|
|
|
|
handleRestoredObject(objects[0]);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
reject(new Error("对象恢复失败"));
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-07-10 01:01:46 +08:00
|
|
|
|
}
|
2026-01-02 11:24:11 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取对象黑白通道画布
|
2026-01-05 11:47:36 +08:00
|
|
|
|
* @param {fabric.Object} object - 要处理的 fabric 对象
|
|
|
|
|
|
* @param {ImageData} revData - 相反的ImageData,白通道的相同位置是否为透明,revData为白色为透明,黑色为不透明
|
2026-01-08 15:25:15 +08:00
|
|
|
|
* @param {number} diff - 差值,默认 25
|
2026-01-16 15:16:33 +08:00
|
|
|
|
* @param {Object} rgba - 自定义 rgba 值,默认 { r: 255, g: 255, b: 255, a: 255 }
|
2026-01-05 11:47:36 +08:00
|
|
|
|
* @returns {HTMLCanvasElement|null} 包含黑白通道的画布,或 null 如果失败
|
2026-01-02 11:24:11 +08:00
|
|
|
|
*/
|
2026-01-16 15:16:33 +08:00
|
|
|
|
export function getObjectAlphaToCanvas(object, revData, diff = 30, rgba = { r: 255, g: 255, b: 255, a: 255 }) {
|
2026-01-02 11:24:11 +08:00
|
|
|
|
const image = object.getElement();
|
|
|
|
|
|
const { width, height } = image;
|
2026-01-05 11:47:36 +08:00
|
|
|
|
if (!width || !height) {
|
2026-01-02 11:24:11 +08:00
|
|
|
|
console.warn("对象没有元素");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
const canvas = document.createElement("canvas");
|
|
|
|
|
|
canvas.width = width;
|
|
|
|
|
|
canvas.height = height;
|
|
|
|
|
|
const ctx = canvas.getContext("2d");
|
|
|
|
|
|
ctx.drawImage(image, 0, 0, width, height);
|
|
|
|
|
|
const data = ctx.getImageData(0, 0, width, height);
|
|
|
|
|
|
for (let i = 0; i < data.data.length; i += 4) {
|
|
|
|
|
|
const r = data.data[i + 0];
|
|
|
|
|
|
const g = data.data[i + 1];
|
|
|
|
|
|
const b = data.data[i + 2];
|
|
|
|
|
|
const a = data.data[i + 3];
|
2026-01-05 11:47:36 +08:00
|
|
|
|
const revR = revData?.data[i + 0] || 0;
|
|
|
|
|
|
const revG = revData?.data[i + 1] || 0;
|
|
|
|
|
|
const revB = revData?.data[i + 2] || 0;
|
|
|
|
|
|
const revA = revData?.data[i + 3] || 0;
|
2026-01-02 11:24:11 +08:00
|
|
|
|
if (r || g || b || a) {
|
2026-01-08 15:25:15 +08:00
|
|
|
|
if (revR > diff || revG > diff || revB > diff || revA > diff) {
|
2026-01-05 11:47:36 +08:00
|
|
|
|
data.data[i + 0] = 0;
|
|
|
|
|
|
data.data[i + 1] = 0;
|
|
|
|
|
|
data.data[i + 2] = 0;
|
|
|
|
|
|
data.data[i + 3] = 0;
|
|
|
|
|
|
} else {
|
2026-01-16 15:16:33 +08:00
|
|
|
|
data.data[i + 0] = rgba.r;
|
|
|
|
|
|
data.data[i + 1] = rgba.g;
|
|
|
|
|
|
data.data[i + 2] = rgba.b;
|
|
|
|
|
|
data.data[i + 3] = rgba.a;
|
2026-01-05 11:47:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2026-01-02 11:24:11 +08:00
|
|
|
|
data.data[i + 0] = 0;
|
|
|
|
|
|
data.data[i + 1] = 0;
|
|
|
|
|
|
data.data[i + 2] = 0;
|
|
|
|
|
|
data.data[i + 3] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ctx.clearRect(0, 0, width, height);
|
|
|
|
|
|
ctx.putImageData(data, 0, 0);
|
|
|
|
|
|
return canvas;
|
|
|
|
|
|
}
|