I am struggling to update my transformer's height to match the total height of the text inside, when I decrease the transformer width, the text breaks into several lines causing them to go out of the transformer height, making it unable to see, I then manually have to increase the transformer (also text object I guess) to make the lines below visible
Is there a way to make it automatic?
This is my text component
import React, { useState } from 'react';
import { useRef, useEffect } from 'react';
import { Text as TextObject, Transformer } from 'react-konva';
import { Html } from 'react-konva-utils';
import { BsFillCheckSquareFill } from 'react-icons/bs';
import { usePropertiesContext } from '@/hooks/PropertiesContent';
interface TextProps {
x: number;
y: number;
fontSize: number;
fill: string;
fontVariant: string;
align: undefined;
fontFamily: string;
opacity: number;
rotation: number;
isSelected: boolean;
boundingBoxWidth: number;
boundingBoxHeight: number;
onSelect: () => void;
onChange: ({ x, y }: { x: number; y: number }) => void;
}
const Text: React.FC<TextProps> = ({
x,
y,
fontSize,
fill,
fontVariant,
align,
fontFamily,
opacity,
rotation,
isSelected,
boundingBoxWidth,
boundingBoxHeight,
onSelect,
onChange,
}) => {
const { settextRotation } = usePropertiesContext();
const [textHeight, setTextHeight] = useState(20);
const [text, setText] = useState('Your Text');
const [edit, setEdit] = useState(false);
const [textWidth, setTextWidth] = useState(120);
const [realHeight, setRealHeight] = useState(20);
const [rotationAngle, setRotationAngle] = useState(rotation); // Store the rotation angle
const [transformerHeight, setTransformerHeight] = useState(textHeight);
const shapeRef = useRef() as any;
const trRef = useRef<Transformer>() as any;
useEffect(() => {
if (!edit && isSelected) {
trRef.current?.nodes([shapeRef.current]);
trRef.current.getLayer().batchDraw();
}
}, [edit, isSelected]);
useEffect(() => {
// Access the Konva.Text instance
const textNode = shapeRef.current;
if (textNode) {
const newTextHeight = textNode.height();
console.log(`Height of Text element: ${newTextHeight}`);
setRealHeight(newTextHeight);
}
// Update the rotation angle in state
setRotationAngle(rotation);
}, [rotation]);
// Function to update the size and position of the TextObject and Transformer
const updateSizeAndPosition = (
newTextWidth: number,
newTextHeight: number
) => {
setTextWidth(newTextWidth);
setTextHeight(newTextHeight);
shapeRef.current?.width(newTextWidth);
shapeRef.current?.height(newTextHeight);
// Update Transformer size and position
trRef.current?.boundBoxFunc(trRef.current.getClientRect(), {
x: x,
y: y,
width: newTextWidth,
height: newTextHeight,
});
setTransformerHeight(newTextHeight); // Update the transformer height here
trRef.current?.getLayer().batchDraw();
};
return (
<React.Fragment>
<Html
divProps={{
style: {
position: 'absolute',
top: y + 'px',
left: x + 'px',
transform: `rotate(${rotationAngle}deg)`, // Apply rotation to the textarea
},
}}
>
{edit ? (
<div className="flex items-start gap-x-2">
<textarea
value={text}
onChange={(e) => {
setText(e.target.value);
const newHeight = e.target.scrollHeight;
const newWidth = e.target.scrollWidth;
updateSizeAndPosition(newWidth, newHeight);
}}
style={{
width: textWidth + 'px',
height: textHeight + 'px',
backgroundColor: 'transparent',
color: fill,
textAlign: align,
fontSize: fontSize,
fontFamily: fontFamily,
fontVariant: fontVariant,
opacity: opacity,
lineHeight: 1,
appearance: 'none',
overflow: 'hidden' /* Hide the scrollbar */,
resize: 'none' /* Disable resizing */,
border: 'none',
outline: 'none',
}}
/>
<button onClick={() => setEdit(false)}>
<BsFillCheckSquareFill className="text-blue-500 hover:text-blue-600 text-lg shadow-sm" />
</button>
</div>
) : (
<></>
)}
</Html>
{!edit ? (
<TextObject
onClick={onSelect}
onTap={onSelect}
onDblClick={() => setEdit(true)}
onDblTap={() => setEdit(true)}
text={text}
ref={shapeRef}
fontSize={fontSize}
fill={fill}
x={x}
y={y}
opacity={opacity}
width={textWidth}
fontFamily={fontFamily}
fontVariant={fontVariant}
align={align}
rotation={rotation}
height={textHeight}
draggable
onDragEnd={(e) => {
onChange({
x: e.target.x(),
y: e.target.y(),
});
}}
onDragMove={(e) => {
// Do not let the object move outside of the pdf page
if (e.target.x() - 10 < 0) e.target.x(10);
if (e.target.y() - 10 < 0) e.target.y(10);
if (e.target.x() + e.target.width() + 10 > boundingBoxWidth)
e.target.x(boundingBoxWidth - e.target.width() - 10);
if (e.target.y() + e.target.height() + 10 > boundingBoxHeight)
e.target.y(boundingBoxHeight - e.target.height() - 10);
}}
/>
) : (
<></>
)}
{isSelected && !edit && (
<Transformer
x={x}
y={y}
width={textWidth}
height={transformerHeight}
rotateAnchorOffset={30}
anchorCornerRadius={90}
padding={5}
ref={trRef}
boundBoxFunc={(oldBox, newBox) => {
if (newBox.width < 5 || newBox.height < 5) {
return oldBox;
}
return newBox;
}}
onTransform={(e) => {
const node = e.target;
const newRotation = Math.round(node.rotation()); // Get the new rotation
settextRotation(newRotation); // Update the rotation angle in state
node.setAttrs({
width: Math.max(node.width() * node.scaleX(), 20),
height: Math.max(node.height() * node.scaleY(), 20),
scaleX: 1,
scaleY: 1,
});
setTextWidth(node.width() * node.scaleX());
setTextHeight(node.height() * node.scaleY());
onChange({
x: e.target.x(),
y: e.target.y(),
});
}}
/>
)}
</React.Fragment>
);
};
export default Text;
If you explicitly set the height of the text, Konva will always respect it. If text overflow defined height, then Konva will cut it and render only visible lines.
There are two ways to resolve the issue.
You can just remove
heightproperty from<Text />component. So it will be "auto" by default. You can still keep it in the state for other purposes.When you update the state with the updated text height, instead of reading it directly from the text instance, you can reset it to
undefinedfirst then get it. After reset, Konva will return the height required for all lines.