画布增加的新功能
This commit is contained in:
@@ -79,6 +79,9 @@ export class LayerSort {
|
||||
} else if (layer.isFixed && layer.fabricObject) {
|
||||
// 固定图层对象
|
||||
zIndexMap.set(layer.fabricObject.id, currentZIndex++);
|
||||
} else if (layer.isFixedOther && layer.fabricObject) {
|
||||
// 其他固定图层对象
|
||||
zIndexMap.set(layer.fabricObject.id, currentZIndex++);
|
||||
} else if (!layer.isBackground && !layer.isFixed) {
|
||||
// 普通图层
|
||||
currentZIndex = this.processLayerObjects(
|
||||
|
||||
37
src/component/Canvas/CanvasEditor/utils/event.js
Normal file
37
src/component/Canvas/CanvasEditor/utils/event.js
Normal file
@@ -0,0 +1,37 @@
|
||||
class EventManager {
|
||||
constructor() {
|
||||
this.eventMap = {};
|
||||
}
|
||||
/**
|
||||
* 注册事件
|
||||
* @param {string} eventName - 事件名称
|
||||
* @param {function} callback - 事件回调函数
|
||||
*/
|
||||
on(eventName, callback) {
|
||||
if (!this.eventMap[eventName]) {
|
||||
this.eventMap[eventName] = [];
|
||||
}
|
||||
this.eventMap[eventName].push(callback);
|
||||
}
|
||||
/**
|
||||
* 触发事件
|
||||
* @param {string} eventName - 事件名称
|
||||
* @param {...any} args - 事件参数
|
||||
*/
|
||||
emit(eventName, ...args) {
|
||||
if (this.eventMap[eventName]) {
|
||||
this.eventMap[eventName].forEach(callback => callback(...args));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 移除事件
|
||||
* @param {string} eventName - 事件名称
|
||||
* @param {function} callback - 事件回调函数
|
||||
*/
|
||||
off(eventName, callback) {
|
||||
if (this.eventMap[eventName]) {
|
||||
this.eventMap[eventName] = this.eventMap[eventName].filter(cb => cb !== callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
export default new EventManager();
|
||||
@@ -429,7 +429,8 @@ export function objectIsInCanvas(canvas, targetObj) {
|
||||
}
|
||||
|
||||
const targetId = targetObj.id;
|
||||
if (!targetId) {
|
||||
const targetLayerId = targetObj.layerId;
|
||||
if (!targetId && !targetLayerId) {
|
||||
return { flag: false, object: null, parent: null };
|
||||
}
|
||||
|
||||
@@ -437,7 +438,11 @@ export function objectIsInCanvas(canvas, targetObj) {
|
||||
const topLevelObjects = canvas.getObjects();
|
||||
|
||||
// 直接在顶层查找
|
||||
const directMatch = topLevelObjects.find((obj) => obj.id === targetId);
|
||||
const directMatch = topLevelObjects.find((obj) => {
|
||||
const isId = !targetId ? true : obj.id === targetId;
|
||||
const isLayerId = !targetLayerId ? true : obj.layerId === targetLayerId;
|
||||
return isId && isLayerId;
|
||||
});
|
||||
if (directMatch) {
|
||||
return { flag: true, object: directMatch, parent: null };
|
||||
}
|
||||
@@ -500,6 +505,22 @@ export function findObjectById(canvas, objectId) {
|
||||
return { object: result.object, parent: result.parent };
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过layerID查找对象(增强版)
|
||||
* @param {fabric.Canvas} canvas 画布实例
|
||||
* @param {string} layerId 图层ID
|
||||
* @returns {Object} { object: fabric.Object|null, parent: fabric.Group|null }
|
||||
*/
|
||||
export function findObjectByLayerId(canvas, layerId) {
|
||||
if (!canvas || !layerId) {
|
||||
return { object: null, parent: null };
|
||||
}
|
||||
|
||||
const result = objectIsInCanvas(canvas, { layerId: layerId });
|
||||
return { object: result.object, parent: result.parent };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 安全移除画布对象(包括组内对象)
|
||||
* @param {fabric.Canvas} canvas 画布实例
|
||||
@@ -738,3 +759,203 @@ export function getLayerObjectsZIndex(canvas, layerId) {
|
||||
const allInfo = getAllObjectsZIndex(canvas);
|
||||
return allInfo.filter((info) => info.layerId === layerId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算两点之间的角度
|
||||
* @param {number} x1 第一个点的x坐标
|
||||
* @param {number} y1 第一个点的y坐标
|
||||
* @param {number} x2 第二个点的x坐标
|
||||
* @param {number} y2 第二个点的y坐标
|
||||
* @returns {number} 角度值(-90 - 270度)
|
||||
*/
|
||||
export function calculateAngle(x1, y1, x2, y2, int = false) {
|
||||
// 计算两点之间的差值
|
||||
const deltaX = x2 - x1;
|
||||
const deltaY = y2 - y1;
|
||||
|
||||
// 使用Math.atan2计算弧度,然后转换为角度
|
||||
let angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI + 90;
|
||||
|
||||
return int ? Math.round(angle) : angle;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过角度计算直线上的两点坐标返回0-1范围的坐标
|
||||
* @param {number} angle 角度值(0-360度)
|
||||
* @returns {{x1: number, y1: number, x2: number, y2: number}} 包含两个点坐标
|
||||
*/
|
||||
export function calculateLinePoints(angle) {
|
||||
// 将角度转换为弧度
|
||||
const radian = (angle - 90) * Math.PI / 180;
|
||||
|
||||
// 计算直线上的两点坐标
|
||||
const x1 = 0.5 - 0.5 * Math.cos(radian);
|
||||
const y1 = 0.5 - 0.5 * Math.sin(radian);
|
||||
const x2 = 0.5 + 0.5 * Math.cos(radian);
|
||||
const y2 = 0.5 + 0.5 * Math.sin(radian);
|
||||
|
||||
return {x1, y1, x2, y2};
|
||||
}
|
||||
|
||||
export function rgbaToHex(rgba){
|
||||
const r = rgba.r.toString(16).padStart(2, "0");
|
||||
const g = rgba.g.toString(16).padStart(2, "0");
|
||||
const b = rgba.b.toString(16).padStart(2, "0");
|
||||
return `#${r}${g}${b}`;
|
||||
}
|
||||
|
||||
export function fillToPallet(fill) {
|
||||
if(!fill.coords || !fill.colorStops) return {};
|
||||
const angle = calculateAngle(fill.coords.x1, fill.coords.y1, fill.coords.x2, fill.coords.y2);
|
||||
const colors = new Set();
|
||||
// console.log("==========fill", fill);
|
||||
const gradientList = fill.colorStops.map((stop) => {
|
||||
colors.add(stop.color);
|
||||
const rgbas = stop.color.replace("rgb(", "").replace("rgba(", "").replace(")", "").split(", ");
|
||||
const rgba = {
|
||||
r: parseInt(rgbas[0]),
|
||||
g: parseInt(rgbas[1]),
|
||||
b: parseInt(rgbas[2]),
|
||||
a: parseFloat(rgbas[3]),
|
||||
};
|
||||
if(isNaN(rgba.r)) rgba.r = 0;
|
||||
if(isNaN(rgba.g)) rgba.g = 0;
|
||||
if(isNaN(rgba.b)) rgba.b = 0;
|
||||
if(isNaN(rgba.a)) rgba.a = 1;
|
||||
return {
|
||||
rgba: rgba,
|
||||
left: parseInt(stop.offset * 100) + "%",
|
||||
};
|
||||
});
|
||||
const isGradient = colors.size > 1;
|
||||
if(isGradient) {
|
||||
return {
|
||||
// hex: rgbaToHex(gradientList[0].rgba),
|
||||
rgba: gradientList[0].rgba,
|
||||
gradient:{ angle, selectIndex: 0, gradientShow: true, gradientList },
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
hex: rgbaToHex(gradientList[0].rgba),
|
||||
rgba: gradientList[0].rgba,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function palletToFill(pallet) {
|
||||
const fill = {
|
||||
coords: calculateLinePoints(0),
|
||||
colorStops: [
|
||||
{ offset: 0, color: "rgba(0, 0, 0, 0)" },
|
||||
{ offset: 1, color: "rgba(0, 0, 0, 0)" }
|
||||
]
|
||||
}
|
||||
if(pallet?.gradient){
|
||||
let obj = pallet.gradient;
|
||||
fill.coords = calculateLinePoints(obj.angle);
|
||||
if(obj.gradientList.length >= 2){
|
||||
fill.colorStops = obj.gradientList.map(item => ({
|
||||
offset: parseInt(item.left) / 100,
|
||||
color: `rgba(${item.rgba.r}, ${item.rgba.g}, ${item.rgba.b}, ${item.rgba.a})`,
|
||||
}));
|
||||
}
|
||||
}else if(pallet?.rgba?.hasOwnProperty("r") && pallet?.rgba?.hasOwnProperty("g") && pallet?.rgba?.hasOwnProperty("b")){
|
||||
let rgba = pallet.rgba;
|
||||
fill.colorStops = [
|
||||
{ offset: 0, color: `rgb(${rgba.r}, ${rgba.g}, ${rgba.b})` },
|
||||
{ offset: 1, color: `rgb(${rgba.r}, ${rgba.g}, ${rgba.b})` }
|
||||
]
|
||||
}
|
||||
return fill;
|
||||
}
|
||||
|
||||
export function fillToCssStyle(fill) {
|
||||
if(!fill.coords || !fill.colorStops) return "";
|
||||
const angle = calculateAngle(fill.coords.x1, fill.coords.y1, fill.coords.x2, fill.coords.y2);
|
||||
if(fill.colorStops.every(v => v.color === fill.colorStops[0].color)){
|
||||
return fill.colorStops[0].color;
|
||||
}else{
|
||||
var str = "linear-gradient(" + angle + "deg, ";
|
||||
fill.colorStops.forEach((v) => {
|
||||
str += `${v.color} ${v.offset * 100}%, `
|
||||
})
|
||||
return str.slice(0, -2) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据左上角坐标计算旋转后的新坐标
|
||||
* @param {number} W - 宽度
|
||||
* @param {number} H - 高度
|
||||
* @param {number} currentX - 当前左上角x坐标
|
||||
* @param {number} currentY - 当前左上角y坐标
|
||||
* @param {number} currentAngleDeg - 当前角度(度)
|
||||
* @param {number} newAngleDeg - 新角度(度)
|
||||
* @returns {Object} 旋转后的左上角坐标 {x, y}
|
||||
*/
|
||||
export function calculateRotatedTopLeftDeg(
|
||||
W,
|
||||
H,
|
||||
currentX,
|
||||
currentY,
|
||||
currentAngleDeg,
|
||||
newAngleDeg
|
||||
) {
|
||||
const currentAngle = (currentAngleDeg * Math.PI) / 180;
|
||||
const newAngle = (newAngleDeg * Math.PI) / 180;
|
||||
// 1. 用当前角度计算中心点位置
|
||||
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;
|
||||
|
||||
// 2. 用新角度计算旋转后的左上角位置
|
||||
const cosNew = Math.cos(newAngle);
|
||||
const sinNew = Math.sin(newAngle);
|
||||
const newX = Cx + (-W / 2) * cosNew - (-H / 2) * sinNew;
|
||||
const newY = Cy + (-W / 2) * sinNew + (-H / 2) * cosNew;
|
||||
|
||||
return { x: newX, y: newY };
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建缩放+旋转的变换矩阵
|
||||
* @param {number} scale - 缩放比例
|
||||
* @param {number} angle - 旋转角度(度)
|
||||
* @returns {Array} 变换矩阵 [a, b, c, d, e, f]
|
||||
*/
|
||||
export function createPatternTransform(scale, angle) {
|
||||
// return fabric.util.composeMatrix({
|
||||
// scaleX: scale,
|
||||
// scaleY: scale,
|
||||
// angle: angle,
|
||||
// });
|
||||
const angle_ = angle * Math.PI / 180;
|
||||
const cos = Math.cos(angle_);
|
||||
const sin = Math.sin(angle_);
|
||||
|
||||
// 先缩放,后旋转
|
||||
return [
|
||||
scale * cos, // a
|
||||
scale * sin, // b
|
||||
-scale * sin, // c
|
||||
scale * cos, // d
|
||||
0, // e (水平位移)
|
||||
0 // f (垂直位移)
|
||||
];
|
||||
}
|
||||
/**
|
||||
* 获取变换矩阵的缩放、旋转
|
||||
* @param {Array} Transform - 变换矩阵、
|
||||
* @returns {Object} 缩放、旋转角度 {scale, angle}
|
||||
*/
|
||||
export function getTransformScaleAngle(Transform) {
|
||||
const a = Transform[0];
|
||||
const b = Transform[1];
|
||||
const c = Transform[2];
|
||||
const d = Transform[3];
|
||||
const scale = Math.sqrt(a * a + b * b);
|
||||
const angle = Math.round(Math.atan2(b, a) * 180 / Math.PI);
|
||||
return { scale, angle };
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
function initAligningGuidelines(canvas) {
|
||||
var ctx = canvas.getSelectionContext(),
|
||||
aligningLineOffset = 5,
|
||||
aligningLineMargin = 4,
|
||||
aligningLineOffset = 1,
|
||||
aligningLineMargin = 1,
|
||||
aligningLineWidth = 1,
|
||||
aligningLineColor = "rgb(0,255,0)",
|
||||
viewportTransform,
|
||||
@@ -14,9 +14,9 @@ function initAligningGuidelines(canvas) {
|
||||
|
||||
function drawVerticalLine(coords) {
|
||||
drawLine(
|
||||
coords.x + 0.5,
|
||||
coords.x,
|
||||
coords.y1 > coords.y2 ? coords.y2 : coords.y1,
|
||||
coords.x + 0.5,
|
||||
coords.x,
|
||||
coords.y2 > coords.y1 ? coords.y2 : coords.y1
|
||||
);
|
||||
}
|
||||
@@ -24,9 +24,9 @@ function initAligningGuidelines(canvas) {
|
||||
function drawHorizontalLine(coords) {
|
||||
drawLine(
|
||||
coords.x1 > coords.x2 ? coords.x2 : coords.x1,
|
||||
coords.y + 0.5,
|
||||
coords.y,
|
||||
coords.x2 > coords.x1 ? coords.x2 : coords.x1,
|
||||
coords.y + 0.5
|
||||
coords.y
|
||||
);
|
||||
}
|
||||
|
||||
@@ -351,7 +351,7 @@ export function initCenteringGuidelines(canvas) {
|
||||
canvasHeightCenter = canvasHeight / 2,
|
||||
canvasWidthCenterMap = {},
|
||||
canvasHeightCenterMap = {},
|
||||
centerLineMargin = 4,
|
||||
centerLineMargin = 1,
|
||||
centerLineColor = "rgba(255,0,241,0.5)",
|
||||
centerLineWidth = 1,
|
||||
ctx = canvas.getSelectionContext(),
|
||||
|
||||
@@ -18,6 +18,16 @@ export const LayerType = {
|
||||
BACKGROUND: "background", // 背景图层 - 位于固定图层之、普通图层之下
|
||||
};
|
||||
|
||||
/**
|
||||
* 特殊图层ID
|
||||
*/
|
||||
export const SpecialLayerId = {
|
||||
SPECIAL_GROUP: "group_special", // 特殊组
|
||||
COLOR: "special_color", // 颜色图层
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 画布操作模式枚举:draw(绘画)、select(选择)、pan(拖拽)....
|
||||
*/
|
||||
@@ -178,12 +188,17 @@ export function createLayer(options = {}) {
|
||||
locked: options.locked !== undefined ? options.locked : false,
|
||||
opacity: options.opacity !== undefined ? options.opacity : 1.0,
|
||||
blendMode: options.blendMode || BlendMode.NORMAL,
|
||||
isHidenDragHandle: options.isHidenDragHandle || false,
|
||||
isDisableUnlock: options.isDisableUnlock || false,
|
||||
isFixedOther: options.isFixedOther || false,
|
||||
isFixedClipMask: options.isFixedClipMask || false,
|
||||
|
||||
// 确保不是背景图层
|
||||
isBackground: false,
|
||||
|
||||
// Fabric.js 对象列表
|
||||
fabricObjects: options.fabricObjects || [],
|
||||
fabricObject: options.fabricObject || null,
|
||||
|
||||
// 嵌套结构 - 适用于组图层
|
||||
children: options.children || [],
|
||||
|
||||
@@ -172,6 +172,10 @@ export function simplifyLayers(layers) {
|
||||
opacity: layer.opacity,
|
||||
isBackground: layer.isBackground || false,
|
||||
isFixed: layer.isFixed || false,
|
||||
isFixedOther: layer.isFixedOther || false,
|
||||
isFixedClipMask: layer.isFixedClipMask || false,
|
||||
isHidenDragHandle: layer.isHidenDragHandle || false,
|
||||
isDisableUnlock: layer.isDisableUnlock || false,
|
||||
clippingMask:
|
||||
layer.clippingMask?.toObject?.(["id", "layerId"]) ||
|
||||
layer.clippingMask ||
|
||||
|
||||
@@ -7,55 +7,92 @@ import { fabric } from "fabric-with-all";
|
||||
* @returns {Promise<fabric.Object>} 恢复的 fabric 对象
|
||||
*/
|
||||
export async function restoreFabricObject(serializedObject, canvas) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const objectType = serializedObject.type;
|
||||
// 定义恢复后的处理函数
|
||||
const handleRestoredObject = (fabricObject) => {
|
||||
if (!fabricObject) {
|
||||
reject(new Error(`无法恢复 ${objectType} 类型的对象`));
|
||||
return;
|
||||
}
|
||||
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;
|
||||
|
||||
// 恢复自定义属性
|
||||
if (serializedObject.id) fabricObject.id = serializedObject.id;
|
||||
if (serializedObject.layerId) fabricObject.layerId = serializedObject.layerId;
|
||||
if (serializedObject.layerName) fabricObject.layerName = serializedObject.layerName;
|
||||
// 更新坐标
|
||||
fabricObject.setCoords();
|
||||
|
||||
// 更新坐标
|
||||
fabricObject.setCoords();
|
||||
// 添加到画布
|
||||
// canvas.add(fabricObject);
|
||||
|
||||
// 添加到画布
|
||||
// canvas.add(fabricObject);
|
||||
resolve(fabricObject);
|
||||
};
|
||||
|
||||
resolve(fabricObject);
|
||||
};
|
||||
|
||||
// 根据类型选择恢复方法
|
||||
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("对象恢复失败"));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 根据类型选择恢复方法
|
||||
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("对象恢复失败"));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象黑白通道画布
|
||||
*/
|
||||
export function getObjectAlphaToCanvas(object) {
|
||||
const image = object.getElement();
|
||||
const { width, height } = image;
|
||||
if(!width || !height){
|
||||
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];
|
||||
if (r || g || b || a) {
|
||||
data.data[i + 0] = 255;
|
||||
data.data[i + 1] = 255;
|
||||
data.data[i + 2] = 255;
|
||||
data.data[i + 3] = 255;
|
||||
}else{
|
||||
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;
|
||||
}
|
||||
@@ -68,7 +68,7 @@ export const createRasterizedImage = async ({
|
||||
isReturenDataURL,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("创建栅格化图像失败:", error);
|
||||
console.warn("创建栅格化图像失败:", error);
|
||||
throw new Error(`栅格化失败: ${error.message}`);
|
||||
}
|
||||
};
|
||||
@@ -163,7 +163,7 @@ const createClippedObjects = async ({
|
||||
console.log("✅ 返回裁剪后的fabric对象,已恢复到优化后的原始大小和位置");
|
||||
return fabricImage;
|
||||
} catch (error) {
|
||||
console.error("创建裁剪对象失败:", error);
|
||||
console.warn("创建裁剪对象失败:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@@ -1239,7 +1239,7 @@ const calculateOptimizedBounds = (clippingObject, fabricObjects) => {
|
||||
|
||||
return optimizedBounds;
|
||||
} catch (error) {
|
||||
console.error("计算优化边界框失败:", error);
|
||||
console.warn("计算优化边界框失败:", error);
|
||||
// 返回原始计算方式作为备选
|
||||
return clippingObject.getBoundingRect(true, true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user