feat: 裁剪组裁剪跟随选择组移动

This commit is contained in:
bighuixiang
2025-07-14 01:00:23 +08:00
parent 96e13cb22a
commit 24e9ba8ae5
80 changed files with 2052 additions and 4292 deletions

View File

@@ -85,18 +85,12 @@ const createClippedObjects = async ({
console.log("🎯 使用新的图像遮罩裁剪方法创建对象");
// 使用优化后的边界计算,确保包含描边区域
const optimizedBounds = calculateOptimizedBounds(
clippingObject,
fabricObjects
);
const optimizedBounds = calculateOptimizedBounds(clippingObject, fabricObjects);
console.log("📐 优化后的选区边界框:", optimizedBounds);
// 获取羽化值
let featherAmount = 0;
if (
selectionManager &&
typeof selectionManager.getFeatherAmount === "function"
) {
if (selectionManager && typeof selectionManager.getFeatherAmount === "function") {
featherAmount = selectionManager.getFeatherAmount();
console.log(`🌟 应用羽化效果: ${featherAmount}px`);
}
@@ -177,10 +171,7 @@ const createClippedDataURLByCanvas = async ({
console.log("🖼️ 使用图像遮罩裁剪方法生成DataURL");
// 使用优化后的边界计算,确保包含描边区域
const optimizedBounds = calculateOptimizedBounds(
clippingObject,
fabricObjects
);
const optimizedBounds = calculateOptimizedBounds(clippingObject, fabricObjects);
// 使用高分辨率以保证质量
const pixelRatio = window.devicePixelRatio || 1;
@@ -239,13 +230,7 @@ const createClippedDataURLByCanvas = async ({
* 创建简单克隆对象
* 当不需要裁剪时,直接克隆原对象
*/
const createSimpleClone = async ({
canvas,
fabricObjects,
isReturenDataURL,
quality,
format,
}) => {
const createSimpleClone = async ({ canvas, fabricObjects, isReturenDataURL, quality, format }) => {
try {
console.log("📋 创建简单克隆对象");
@@ -459,10 +444,7 @@ const createLegacyRasterization = async ({
// 这里保留原有的离屏渲染逻辑作为备选方案
const currentZoom = canvas.getZoom?.() || 1;
scaleFactor = Math.max(
scaleFactor || canvas?.getRetinaScaling?.(),
currentZoom
);
scaleFactor = Math.max(scaleFactor || canvas?.getRetinaScaling?.(), currentZoom);
scaleFactor = Math.min(scaleFactor, 3);
const { absoluteBounds, relativeBounds } = calculateBounds(fabricObjects);
@@ -592,9 +574,7 @@ const createOffscreenRasterization = async ({
height: canvasHeight,
});
console.log(
`🎨 离屏画布尺寸: ${canvasWidth}x${canvasHeight}, 缩放: ${scaleFactor}`
);
console.log(`🎨 离屏画布尺寸: ${canvasWidth}x${canvasHeight}, 缩放: ${scaleFactor}`);
// 克隆对象到离屏画布
const clonedObjects = [];
@@ -749,11 +729,7 @@ export const getObjectsBounds = (fabricObjects) => {
* @param {Number} qualityMultiplier 质量倍数
* @returns {Promise<String>} 遮罩图像的DataURL
*/
const createMaskImageFromPath = async ({
clippingObject,
selectionBounds,
qualityMultiplier,
}) => {
const createMaskImageFromPath = async ({ clippingObject, selectionBounds, qualityMultiplier }) => {
try {
console.log("🎭 创建路径遮罩图像");
@@ -769,11 +745,7 @@ const createMaskImageFromPath = async ({
});
// 克隆路径对象并处理描边转填充
const maskPath = await createSolidMaskPath(
clippingObject,
selectionBounds,
qualityMultiplier
);
const maskPath = await createSolidMaskPath(clippingObject, selectionBounds, qualityMultiplier);
// 添加路径到遮罩画布
maskCanvas.add(maskPath);
@@ -804,11 +776,7 @@ const createMaskImageFromPath = async ({
* @param {Number} qualityMultiplier 质量倍数
* @returns {Promise<String>} 内容图像的DataURL
*/
const renderContentToImage = async ({
fabricObjects,
selectionBounds,
qualityMultiplier,
}) => {
const renderContentToImage = async ({ fabricObjects, selectionBounds, qualityMultiplier }) => {
try {
console.log("🖼️ 渲染内容图像");
@@ -964,11 +932,7 @@ const createAdvancedMaskImage = async ({
});
// 克隆路径对象并处理描边转填充
const maskPath = await createSolidMaskPath(
clippingObject,
selectionBounds,
qualityMultiplier
);
const maskPath = await createSolidMaskPath(clippingObject, selectionBounds, qualityMultiplier);
// 如果有羽化值,添加模糊效果
if (featherAmount > 0) {
@@ -987,10 +951,7 @@ const createAdvancedMaskImage = async ({
// 如果有羽化,需要进行后处理
if (featherAmount > 0) {
return await applyCanvasBlur(
maskCanvas,
featherAmount * qualityMultiplier
);
return await applyCanvasBlur(maskCanvas, featherAmount * qualityMultiplier);
}
// 生成遮罩图像
@@ -1066,11 +1027,7 @@ const applyCanvasBlur = async (canvas, blurAmount) => {
* @param {Number} qualityMultiplier 质量倍数
* @returns {Promise<fabric.Object>} 处理后的遮罩路径对象
*/
const createSolidMaskPath = async (
clippingObject,
selectionBounds,
qualityMultiplier
) => {
const createSolidMaskPath = async (clippingObject, selectionBounds, qualityMultiplier) => {
try {
console.log("🔧 创建实体遮罩路径,处理描边转填充");
@@ -1081,29 +1038,19 @@ const createSolidMaskPath = async (
const hasStroke = maskPath.stroke && maskPath.strokeWidth > 0;
if (hasStroke) {
console.log(
`📏 检测到描边: ${maskPath.stroke}, 宽度: ${maskPath.strokeWidth}`
);
console.log(`📏 检测到描边: ${maskPath.stroke}, 宽度: ${maskPath.strokeWidth}`);
// 对于有描边的路径,我们需要更精确的处理
const strokeWidth = maskPath.strokeWidth;
// 方法1: 如果是简单的几何形状(矩形、圆形等),可以通过调整尺寸来补偿描边
if (
maskPath.type === "rect" ||
maskPath.type === "circle" ||
maskPath.type === "ellipse"
) {
if (maskPath.type === "rect" || maskPath.type === "circle" || maskPath.type === "ellipse") {
// 对于矩形和椭圆,增加宽高来包含描边
const strokeOffset = strokeWidth;
maskPath.set({
left:
(maskPath.left - selectionBounds.left - strokeOffset / 2) *
qualityMultiplier,
top:
(maskPath.top - selectionBounds.top - strokeOffset / 2) *
qualityMultiplier,
left: (maskPath.left - selectionBounds.left - strokeOffset / 2) * qualityMultiplier,
top: (maskPath.top - selectionBounds.top - strokeOffset / 2) * qualityMultiplier,
scaleX: (maskPath.scaleX || 1) * qualityMultiplier,
scaleY: (maskPath.scaleY || 1) * qualityMultiplier,
width: (maskPath.width || 0) + strokeOffset,
@@ -1122,12 +1069,8 @@ const createSolidMaskPath = async (
const strokeOffset = strokeWidth / 2;
maskPath.set({
left:
(maskPath.left - selectionBounds.left - strokeOffset) *
qualityMultiplier,
top:
(maskPath.top - selectionBounds.top - strokeOffset) *
qualityMultiplier,
left: (maskPath.left - selectionBounds.left - strokeOffset) * qualityMultiplier,
top: (maskPath.top - selectionBounds.top - strokeOffset) * qualityMultiplier,
scaleX: (maskPath.scaleX || 1) * qualityMultiplier * expandRatio,
scaleY: (maskPath.scaleY || 1) * qualityMultiplier * expandRatio,
fill: "#ffffff",