fix text box size
This commit is contained in:
@@ -76,6 +76,8 @@ export const useDrawingHandlers = () => {
|
|||||||
opacity: defaults.opacity,
|
opacity: defaults.opacity,
|
||||||
letterSpacing: defaults.letterSpacing,
|
letterSpacing: defaults.letterSpacing,
|
||||||
wordSpacing: defaults.wordSpacing,
|
wordSpacing: defaults.wordSpacing,
|
||||||
|
width: 150,
|
||||||
|
height: defaults.fontSize || 24,
|
||||||
};
|
};
|
||||||
addObject(newText);
|
addObject(newText);
|
||||||
setSelectedObjectId(newText.id);
|
setSelectedObjectId(newText.id);
|
||||||
|
|||||||
@@ -35,12 +35,9 @@ const getColorWithOpacity = (fill?: string, opacity?: number): string => {
|
|||||||
|
|
||||||
const TextShape = ({
|
const TextShape = ({
|
||||||
obj,
|
obj,
|
||||||
isSelected,
|
|
||||||
transformerRef,
|
|
||||||
onSelect,
|
onSelect,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
draggable,
|
draggable,
|
||||||
layerRef,
|
|
||||||
}: TextShapeProps) => {
|
}: TextShapeProps) => {
|
||||||
const shapeRef = useRef<Konva.Text>(null);
|
const shapeRef = useRef<Konva.Text>(null);
|
||||||
|
|
||||||
@@ -53,6 +50,31 @@ const TextShape = ({
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Update actual text dimensions after render
|
||||||
|
useEffect(() => {
|
||||||
|
if (shapeRef.current) {
|
||||||
|
const node = shapeRef.current;
|
||||||
|
const clientRect = node.getClientRect({ skipTransform: true });
|
||||||
|
|
||||||
|
// اگر width یا height واقعی با مقدار ذخیره شده متفاوت است، آپدیت کن
|
||||||
|
if (clientRect.width && clientRect.height) {
|
||||||
|
const needsUpdate =
|
||||||
|
!obj.width ||
|
||||||
|
!obj.height ||
|
||||||
|
Math.abs((obj.width || 0) - clientRect.width) > 5 ||
|
||||||
|
Math.abs((obj.height || 0) - clientRect.height) > 5;
|
||||||
|
|
||||||
|
if (needsUpdate) {
|
||||||
|
onUpdate(obj.id, {
|
||||||
|
width: Math.ceil(clientRect.width),
|
||||||
|
height: Math.ceil(clientRect.height),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [obj.text, obj.fontSize, obj.fontFamily, obj.fontWeight, obj.letterSpacing]);
|
||||||
|
|
||||||
const fontFamily = useMemo(() => getFontFamily(obj.fontFamily), [obj.fontFamily]);
|
const fontFamily = useMemo(() => getFontFamily(obj.fontFamily), [obj.fontFamily]);
|
||||||
const fontWeight = useMemo(() => getFontWeight(obj.fontWeight), [obj.fontWeight]);
|
const fontWeight = useMemo(() => getFontWeight(obj.fontWeight), [obj.fontWeight]);
|
||||||
const fillColor = useMemo(() => getColorWithOpacity(obj.fill, obj.opacity), [obj.fill, obj.opacity]);
|
const fillColor = useMemo(() => getColorWithOpacity(obj.fill, obj.opacity), [obj.fill, obj.opacity]);
|
||||||
@@ -60,6 +82,8 @@ const TextShape = ({
|
|||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
ref={shapeRef}
|
ref={shapeRef}
|
||||||
|
id={obj.id}
|
||||||
|
name="text-object"
|
||||||
x={obj.x}
|
x={obj.x}
|
||||||
y={obj.y}
|
y={obj.y}
|
||||||
text={obj.text || ""}
|
text={obj.text || ""}
|
||||||
@@ -68,8 +92,6 @@ const TextShape = ({
|
|||||||
fontFamily={fontFamily}
|
fontFamily={fontFamily}
|
||||||
fontStyle={fontWeight}
|
fontStyle={fontWeight}
|
||||||
letterSpacing={obj.letterSpacing ?? 0}
|
letterSpacing={obj.letterSpacing ?? 0}
|
||||||
width={obj.width}
|
|
||||||
height={obj.height}
|
|
||||||
align="right"
|
align="right"
|
||||||
rotation={obj.rotation || 0}
|
rotation={obj.rotation || 0}
|
||||||
draggable={draggable}
|
draggable={draggable}
|
||||||
@@ -95,71 +117,17 @@ const TextShape = ({
|
|||||||
node.scaleX(1);
|
node.scaleX(1);
|
||||||
node.scaleY(1);
|
node.scaleY(1);
|
||||||
|
|
||||||
|
// محاسبه اندازه واقعی متن بعد از transform
|
||||||
|
const clientRect = node.getClientRect({ skipTransform: true });
|
||||||
|
const newWidth = clientRect.width * scaleX;
|
||||||
|
const newHeight = clientRect.height * scaleY;
|
||||||
|
|
||||||
onUpdate(obj.id, {
|
onUpdate(obj.id, {
|
||||||
x: node.x(),
|
x: node.x(),
|
||||||
y: node.y(),
|
y: node.y(),
|
||||||
rotation: node.rotation(),
|
rotation: node.rotation(),
|
||||||
width: obj.width ? Math.max(5, obj.width * scaleX) : undefined,
|
width: Math.max(5, newWidth),
|
||||||
height: obj.height ? Math.max(5, obj.height * scaleY) : undefined,
|
height: Math.max(5, newHeight),
|
||||||
});
|
|
||||||
}}
|
|
||||||
onDblClick={(e) => {
|
|
||||||
const textNode = e.target as Konva.Text;
|
|
||||||
const stage = textNode.getStage();
|
|
||||||
if (!stage) return;
|
|
||||||
|
|
||||||
const stageBox = stage.container().getBoundingClientRect();
|
|
||||||
const textPosition = textNode.absolutePosition();
|
|
||||||
const scaleX = stage.scaleX() || 1;
|
|
||||||
const scaleY = stage.scaleY() || 1;
|
|
||||||
const areaPosition = {
|
|
||||||
x: stageBox.left + textPosition.x * scaleX,
|
|
||||||
y: stageBox.top + textPosition.y * scaleY,
|
|
||||||
};
|
|
||||||
|
|
||||||
const textarea = document.createElement("textarea");
|
|
||||||
document.body.appendChild(textarea);
|
|
||||||
textNode.hide();
|
|
||||||
layerRef.current?.draw();
|
|
||||||
textarea.value = textNode.text();
|
|
||||||
textarea.style.position = "absolute";
|
|
||||||
textarea.style.top = `${areaPosition.y}px`;
|
|
||||||
textarea.style.left = `${areaPosition.x}px`;
|
|
||||||
textarea.style.width = `${textNode.width() * scaleX}px`;
|
|
||||||
textarea.style.fontSize = `${textNode.fontSize() * scaleY}px`;
|
|
||||||
textarea.style.border = "none";
|
|
||||||
textarea.style.padding = "0px";
|
|
||||||
textarea.style.margin = "0px";
|
|
||||||
textarea.style.overflow = "hidden";
|
|
||||||
textarea.style.background = "transparent";
|
|
||||||
textarea.style.outline = "none";
|
|
||||||
textarea.style.resize = "none";
|
|
||||||
textarea.style.fontFamily = fontFamily;
|
|
||||||
textarea.style.fontWeight = fontWeight === "bold" ? "bold" : "normal";
|
|
||||||
textarea.style.letterSpacing = `${obj.letterSpacing ?? 0}px`;
|
|
||||||
textarea.style.textAlign = textNode.align() || "right";
|
|
||||||
textarea.style.direction = "rtl";
|
|
||||||
textarea.focus();
|
|
||||||
|
|
||||||
const removeTextarea = () => {
|
|
||||||
document.body.removeChild(textarea);
|
|
||||||
textNode.show();
|
|
||||||
textNode.text(textarea.value);
|
|
||||||
onUpdate(obj.id, { text: textarea.value });
|
|
||||||
layerRef.current?.draw();
|
|
||||||
};
|
|
||||||
|
|
||||||
textarea.addEventListener("keydown", (e) => {
|
|
||||||
if (e.key === "Enter" && !e.shiftKey) {
|
|
||||||
removeTextarea();
|
|
||||||
}
|
|
||||||
if (e.key === "Escape") {
|
|
||||||
removeTextarea();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
textarea.addEventListener("blur", () => {
|
|
||||||
removeTextarea();
|
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -696,10 +696,25 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
|
|||||||
let maxY = -Infinity;
|
let maxY = -Infinity;
|
||||||
|
|
||||||
objectsToGroup.forEach((obj) => {
|
objectsToGroup.forEach((obj) => {
|
||||||
const cx = (obj.x || 0) + (obj.width || 0) / 2;
|
// برای متنهایی که width و height ندارند، از مقادیر پیشفرض استفاده کن
|
||||||
const cy = (obj.y || 0) + (obj.height || 0) / 2;
|
let w = obj.width || 0;
|
||||||
const w = obj.width || 0;
|
let h = obj.height || 0;
|
||||||
const h = obj.height || 0;
|
|
||||||
|
if (obj.type === "text") {
|
||||||
|
// اگر متن width نداره، از fontSize و طول متن برای تخمین استفاده کن
|
||||||
|
if (!w) {
|
||||||
|
const textLength = (obj.text || "").length;
|
||||||
|
const fontSize = obj.fontSize || 24;
|
||||||
|
w = Math.max(textLength * fontSize * 0.6, 50);
|
||||||
|
}
|
||||||
|
// اگر height نداره، از fontSize استفاده کن
|
||||||
|
if (!h) {
|
||||||
|
h = obj.fontSize || 24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cx = (obj.x || 0) + w / 2;
|
||||||
|
const cy = (obj.y || 0) + h / 2;
|
||||||
const rot = ((obj.rotation || 0) * Math.PI) / 180;
|
const rot = ((obj.rotation || 0) * Math.PI) / 180;
|
||||||
|
|
||||||
// محاسبه چهار گوشه rectangle با rotation
|
// محاسبه چهار گوشه rectangle با rotation
|
||||||
|
|||||||
Reference in New Issue
Block a user