From f492d4a29fcf96cb21bbb362d730a03d420c21be Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Mon, 5 Jan 2026 15:22:30 +0330 Subject: [PATCH] fix resize --- src/pages/editor/components/EditorCanvas.tsx | 113 ++++++++++++++++-- .../components/canvas/ObjectRenderer.tsx | 60 +++++++++- .../editor/components/tools/AbstractShape.tsx | 20 ---- .../editor/components/tools/ArrowShape.tsx | 23 ---- .../editor/components/tools/CircleShape.tsx | 18 --- .../editor/components/tools/ImageObject.tsx | 18 --- .../editor/components/tools/LineShape.tsx | 23 ---- .../editor/components/tools/LinkShape.tsx | 16 --- .../components/tools/RectangleShape.tsx | 18 --- .../editor/components/tools/TextShape.tsx | 23 ---- .../editor/components/tools/TriangleShape.tsx | 20 ---- 11 files changed, 160 insertions(+), 192 deletions(-) diff --git a/src/pages/editor/components/EditorCanvas.tsx b/src/pages/editor/components/EditorCanvas.tsx index 7dfbea8..703f900 100644 --- a/src/pages/editor/components/EditorCanvas.tsx +++ b/src/pages/editor/components/EditorCanvas.tsx @@ -354,6 +354,18 @@ const EditorCanvas = () => { const groupWidth = maxX - minX; const groupHeight = maxY - minY; + // Update group node directly in Konva first to prevent transformer flicker + if (layerRef.current) { + const groupNode = layerRef.current.findOne(`#${selectedObject.id}`); + if (groupNode) { + groupNode.x(minX); + groupNode.y(minY); + groupNode.width(groupWidth); + groupNode.height(groupHeight); + groupNode.rotation(0); + } + } + // Update group object with new bounds (rotation should remain 0) updateObject(selectedObject.id, { x: minX, @@ -362,6 +374,15 @@ const EditorCanvas = () => { height: groupHeight, rotation: 0, }); + + // Force transformer to update immediately + if (transformerRef.current && layerRef.current) { + const groupNode = layerRef.current.findOne(`#${selectedObject.id}`); + if (groupNode) { + transformerRef.current.nodes([groupNode]); + transformerRef.current.getLayer()?.batchDraw(); + } + } } else if (selectedObject.type === "grid") { // Handle grid objects separately const scaleX = node.scaleX(); @@ -386,13 +407,91 @@ const EditorCanvas = () => { node.scaleX(1); node.scaleY(1); - updateObject(selectedObject.id, { - x: node.x(), - y: node.y(), - rotation: node.rotation(), - width: Math.max(5, (selectedObject.width || 100) * scaleX), - height: Math.max(5, (selectedObject.height || 100) * scaleY), - }); + // Handle special shapes that use radius (Circle, Triangle, Abstract) + if (selectedObject.type === "rectangle") { + if (selectedObject.shapeType === "circle") { + // Circle: maintain aspect ratio and use average scale + const avgScale = (scaleX + scaleY) / 2; + const newRadius = Math.max(5, ((selectedObject.width || 50) / 2) * avgScale); + const newSize = newRadius * 2; + updateObject(selectedObject.id, { + x: node.x(), + y: node.y(), + rotation: node.rotation(), + width: newSize, + height: newSize, + }); + } else if (selectedObject.shapeType === "triangle" || selectedObject.shapeType === "abstract") { + // Triangle and Abstract: maintain aspect ratio and use average scale + const avgScale = (scaleX + scaleY) / 2; + const baseRadius = Math.min((selectedObject.width || 100) / 2, (selectedObject.height || 100) / 2); + const newRadius = Math.max(5, baseRadius * avgScale); + const newSize = newRadius * 2; + updateObject(selectedObject.id, { + x: node.x() - newSize / 2, + y: node.y() - newSize / 2, + rotation: node.rotation(), + width: newSize, + height: newSize, + }); + } else { + // Regular rectangle + updateObject(selectedObject.id, { + x: node.x(), + y: node.y(), + rotation: node.rotation(), + width: Math.max(5, (selectedObject.width || 100) * scaleX), + height: Math.max(5, (selectedObject.height || 100) * scaleY), + }); + } + } else if (selectedObject.type === "line" || selectedObject.type === "arrow") { + // Line and Arrow: scale the end point + const startX = selectedObject.x || 0; + const startY = selectedObject.y || 0; + const endX = selectedObject.width || 0; + const endY = selectedObject.height || 0; + const dx = (endX - startX) * scaleX; + const dy = (endY - startY) * scaleY; + updateObject(selectedObject.id, { + x: node.x(), + y: node.y(), + rotation: node.rotation(), + width: node.x() + dx, + height: node.y() + dy, + }); + } else if (selectedObject.type === "text" || selectedObject.type === "link") { + // Text and Link: calculate actual dimensions after transform + const clientRect = node.getClientRect({ skipTransform: true }); + const newWidth = clientRect.width * scaleX; + const newHeight = clientRect.height * scaleY; + + if (selectedObject.type === "link") { + // For link, also update fontSize + updateObject(selectedObject.id, { + x: node.x(), + y: node.y(), + rotation: node.rotation(), + fontSize: selectedObject.fontSize ? Math.max(5, selectedObject.fontSize * scaleX) : undefined, + }); + } else { + updateObject(selectedObject.id, { + x: node.x(), + y: node.y(), + rotation: node.rotation(), + width: Math.max(5, newWidth), + height: Math.max(5, newHeight), + }); + } + } else { + // Default for other objects (image, etc.) + updateObject(selectedObject.id, { + x: node.x(), + y: node.y(), + rotation: node.rotation(), + width: Math.max(5, (selectedObject.width || 100) * scaleX), + height: Math.max(5, (selectedObject.height || 100) * scaleY), + }); + } } }); }; diff --git a/src/pages/editor/components/canvas/ObjectRenderer.tsx b/src/pages/editor/components/canvas/ObjectRenderer.tsx index 7004701..2fd004a 100644 --- a/src/pages/editor/components/canvas/ObjectRenderer.tsx +++ b/src/pages/editor/components/canvas/ObjectRenderer.tsx @@ -339,13 +339,61 @@ const ObjectRenderer = ({ // محاسبه موقعیت جدید object const newX = node.x(); const newY = node.y(); - const newWidth = Math.max(5, (obj.width || 100) * scaleX); - const newHeight = Math.max(5, (obj.height || 100) * scaleY); + + // Handle special shapes that use radius (Circle, Triangle, Abstract) + let newWidth: number; + let newHeight: number; + let finalX = newX; + let finalY = newY; + + if (obj.type === "rectangle") { + if (obj.shapeType === "circle") { + // Circle: maintain aspect ratio and use average scale + const avgScale = (scaleX + scaleY) / 2; + const newRadius = Math.max(5, ((obj.width || 50) / 2) * avgScale); + const newSize = newRadius * 2; + newWidth = newSize; + newHeight = newSize; + } else if (obj.shapeType === "triangle" || obj.shapeType === "abstract") { + // Triangle and Abstract: maintain aspect ratio and use average scale + const avgScale = (scaleX + scaleY) / 2; + const baseRadius = Math.min((obj.width || 100) / 2, (obj.height || 100) / 2); + const newRadius = Math.max(5, baseRadius * avgScale); + const newSize = newRadius * 2; + newWidth = newSize; + newHeight = newSize; + // Adjust position for centered shapes + finalX = newX - newSize / 2; + finalY = newY - newSize / 2; + } else { + // Regular rectangle + newWidth = Math.max(5, (obj.width || 100) * scaleX); + newHeight = Math.max(5, (obj.height || 100) * scaleY); + } + } else if (obj.type === "line" || obj.type === "arrow") { + // Line and Arrow: scale the end point + const startX = obj.x || 0; + const startY = obj.y || 0; + const endX = obj.width || 0; + const endY = obj.height || 0; + const dx = (endX - startX) * scaleX; + const dy = (endY - startY) * scaleY; + newWidth = newX + dx; + newHeight = newY + dy; + } else if (obj.type === "text" || obj.type === "link") { + // Text and Link: use scale directly + newWidth = Math.max(5, (obj.width || 100) * scaleX); + newHeight = Math.max(5, (obj.height || 100) * scaleY); + } else { + // Default for other objects (image, etc.) + newWidth = Math.max(5, (obj.width || 100) * scaleX); + newHeight = Math.max(5, (obj.height || 100) * scaleY); + } // به‌روزرسانی object onUpdate(obj.id, { - x: newX, - y: newY, + x: finalX, + y: finalY, rotation: node.rotation(), width: newWidth, height: newHeight, @@ -356,8 +404,8 @@ const ObjectRenderer = ({ // محاسبه موقعیت جدید mask // موقعیت نسبی را scale می‌کنم و به موقعیت جدید object اضافه می‌کنم // این باعث می‌شود که موقعیت نسبی mask نسبت به object حفظ شود - const newMaskX = newX + (relativeX * scaleX); - const newMaskY = newY + (relativeY * scaleY); + const newMaskX = finalX + (relativeX * scaleX); + const newMaskY = finalY + (relativeY * scaleY); const newMaskWidth = (maskShape.width || 100) * scaleX; const newMaskHeight = (maskShape.height || 100) * scaleY; diff --git a/src/pages/editor/components/tools/AbstractShape.tsx b/src/pages/editor/components/tools/AbstractShape.tsx index 6cbcad3..81d7b8d 100644 --- a/src/pages/editor/components/tools/AbstractShape.tsx +++ b/src/pages/editor/components/tools/AbstractShape.tsx @@ -58,26 +58,6 @@ const AbstractShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape y: node.y() - (obj.height || 100) / 2, }); }} - onTransformEnd={() => { - const node = shapeRef.current; - if (!node) return; - - const scaleX = node.scaleX(); - const scaleY = node.scaleY(); - - node.scaleX(1); - node.scaleY(1); - - const newRadius = Math.max(5, baseRadius * Math.min(scaleX, scaleY)); - const newSize = newRadius * 2; - onUpdate(obj.id, { - x: node.x() - newSize / 2, - y: node.y() - newSize / 2, - rotation: node.rotation(), - width: newSize, - height: newSize, - }); - }} /> ); }; diff --git a/src/pages/editor/components/tools/ArrowShape.tsx b/src/pages/editor/components/tools/ArrowShape.tsx index 1858bfd..6b58a1c 100644 --- a/src/pages/editor/components/tools/ArrowShape.tsx +++ b/src/pages/editor/components/tools/ArrowShape.tsx @@ -46,29 +46,6 @@ const ArrowShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapePro height: newY + dy, }); }} - onTransformEnd={() => { - const node = shapeRef.current; - if (!node) return; - - const scaleX = node.scaleX(); - const scaleY = node.scaleY(); - - node.scaleX(1); - node.scaleY(1); - - const newX = node.x(); - const newY = node.y(); - const dx = (endX - startX) * scaleX; - const dy = (endY - startY) * scaleY; - - onUpdate(obj.id, { - x: newX, - y: newY, - rotation: node.rotation(), - width: newX + dx, - height: newY + dy, - }); - }} /> ); }; diff --git a/src/pages/editor/components/tools/CircleShape.tsx b/src/pages/editor/components/tools/CircleShape.tsx index 440be22..f52f972 100644 --- a/src/pages/editor/components/tools/CircleShape.tsx +++ b/src/pages/editor/components/tools/CircleShape.tsx @@ -52,24 +52,6 @@ const CircleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapePr y: node.y(), }); }} - onTransformEnd={() => { - const node = shapeRef.current; - if (!node) return; - - const scaleX = node.scaleX(); - - node.scaleX(1); - node.scaleY(1); - - const newRadius = Math.max(5, ((obj.width || 50) / 2) * scaleX); - onUpdate(obj.id, { - x: node.x(), - y: node.y(), - rotation: node.rotation(), - width: newRadius * 2, - height: newRadius * 2, - }); - }} /> ); }; diff --git a/src/pages/editor/components/tools/ImageObject.tsx b/src/pages/editor/components/tools/ImageObject.tsx index f97174e..9e63e6a 100644 --- a/src/pages/editor/components/tools/ImageObject.tsx +++ b/src/pages/editor/components/tools/ImageObject.tsx @@ -45,24 +45,6 @@ const ImageObject = ({ y: node.y(), }); }} - onTransformEnd={() => { - const node = shapeRef.current; - if (!node) return; - - const scaleX = node.scaleX(); - const scaleY = node.scaleY(); - - node.scaleX(1); - node.scaleY(1); - - onUpdate(obj.id, { - x: node.x(), - y: node.y(), - rotation: node.rotation(), - width: Math.max(5, (obj.width || image.width) * scaleX), - height: Math.max(5, (obj.height || image.height) * scaleY), - }); - }} /> ); }; diff --git a/src/pages/editor/components/tools/LineShape.tsx b/src/pages/editor/components/tools/LineShape.tsx index c073952..3492722 100644 --- a/src/pages/editor/components/tools/LineShape.tsx +++ b/src/pages/editor/components/tools/LineShape.tsx @@ -44,29 +44,6 @@ const LineShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProp height: newY + dy, }); }} - onTransformEnd={() => { - const node = shapeRef.current; - if (!node) return; - - const scaleX = node.scaleX(); - const scaleY = node.scaleY(); - - node.scaleX(1); - node.scaleY(1); - - const newX = node.x(); - const newY = node.y(); - const dx = (endX - startX) * scaleX; - const dy = (endY - startY) * scaleY; - - onUpdate(obj.id, { - x: newX, - y: newY, - rotation: node.rotation(), - width: newX + dx, - height: newY + dy, - }); - }} /> ); }; diff --git a/src/pages/editor/components/tools/LinkShape.tsx b/src/pages/editor/components/tools/LinkShape.tsx index 33dff9c..1f185ff 100644 --- a/src/pages/editor/components/tools/LinkShape.tsx +++ b/src/pages/editor/components/tools/LinkShape.tsx @@ -50,22 +50,6 @@ const LinkShape = ({ y: node.y(), }); }} - onTransformEnd={() => { - const node = shapeRef.current; - if (!node) return; - - const scaleX = node.scaleX(); - - node.scaleX(1); - node.scaleY(1); - - onUpdate(obj.id, { - x: node.x(), - y: node.y(), - rotation: node.rotation(), - fontSize: obj.fontSize ? Math.max(5, obj.fontSize * scaleX) : undefined, - }); - }} /> ); }; diff --git a/src/pages/editor/components/tools/RectangleShape.tsx b/src/pages/editor/components/tools/RectangleShape.tsx index 12c0dd6..60f57bf 100644 --- a/src/pages/editor/components/tools/RectangleShape.tsx +++ b/src/pages/editor/components/tools/RectangleShape.tsx @@ -53,24 +53,6 @@ const RectangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shap y: node.y(), }); }} - onTransformEnd={() => { - const node = shapeRef.current; - if (!node) return; - - const scaleX = node.scaleX(); - const scaleY = node.scaleY(); - - node.scaleX(1); - node.scaleY(1); - - onUpdate(obj.id, { - x: node.x(), - y: node.y(), - rotation: node.rotation(), - width: Math.max(5, (obj.width || 100) * scaleX), - height: Math.max(5, (obj.height || 100) * scaleY), - }); - }} /> ); }; diff --git a/src/pages/editor/components/tools/TextShape.tsx b/src/pages/editor/components/tools/TextShape.tsx index 90f69ff..98632b5 100644 --- a/src/pages/editor/components/tools/TextShape.tsx +++ b/src/pages/editor/components/tools/TextShape.tsx @@ -107,29 +107,6 @@ const TextShape = ({ y: node.y(), }); }} - onTransformEnd={() => { - const node = shapeRef.current; - if (!node) return; - - const scaleX = node.scaleX(); - const scaleY = node.scaleY(); - - node.scaleX(1); - node.scaleY(1); - - // محاسبه اندازه واقعی متن بعد از transform - const clientRect = node.getClientRect({ skipTransform: true }); - const newWidth = clientRect.width * scaleX; - const newHeight = clientRect.height * scaleY; - - onUpdate(obj.id, { - x: node.x(), - y: node.y(), - rotation: node.rotation(), - width: Math.max(5, newWidth), - height: Math.max(5, newHeight), - }); - }} /> ); }; diff --git a/src/pages/editor/components/tools/TriangleShape.tsx b/src/pages/editor/components/tools/TriangleShape.tsx index 1714ec2..5ac9acb 100644 --- a/src/pages/editor/components/tools/TriangleShape.tsx +++ b/src/pages/editor/components/tools/TriangleShape.tsx @@ -54,26 +54,6 @@ const TriangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape y: node.y() - (obj.height || 100) / 2, }); }} - onTransformEnd={() => { - const node = shapeRef.current; - if (!node) return; - - const scaleX = node.scaleX(); - const scaleY = node.scaleY(); - - node.scaleX(1); - node.scaleY(1); - - const newRadius = Math.max(5, radius * Math.min(scaleX, scaleY)); - const newSize = newRadius * 2; - onUpdate(obj.id, { - x: node.x() - newSize / 2, - y: node.y() - newSize / 2, - rotation: node.rotation(), - width: newSize, - height: newSize, - }); - }} /> ); };