135 lines
3.9 KiB
TypeScript
135 lines
3.9 KiB
TypeScript
import { useEffect, useRef } from "react";
|
|
import { Image as KonvaImage, Group, Rect } from "react-konva";
|
|
import Konva from "konva";
|
|
import useImage from "use-image";
|
|
import type { ShapeProps } from "./types";
|
|
import { clampPositionToStage } from "../../utils/stageBounds";
|
|
import { createRoundedRectClipFunc, getObjectBorderRadius } from "../../utils/borderRadius";
|
|
|
|
const ImageObject = ({
|
|
obj,
|
|
isSelected,
|
|
onSelect,
|
|
onUpdate,
|
|
draggable,
|
|
stageWidth,
|
|
stageHeight,
|
|
onImageReady,
|
|
}: ShapeProps) => {
|
|
const [image, status] = useImage(obj.imageUrl || "");
|
|
const shapeRef = useRef<Konva.Image>(null);
|
|
const groupRef = useRef<Konva.Group>(null);
|
|
|
|
useEffect(() => {
|
|
if (status === "loaded" && onImageReady) {
|
|
onImageReady();
|
|
}
|
|
}, [status, onImageReady]);
|
|
|
|
if (!image) return null;
|
|
|
|
const width = obj.width || image.width;
|
|
const height = obj.height || image.height;
|
|
const hasStageBounds = stageWidth != null && stageHeight != null;
|
|
const cornerRadius = getObjectBorderRadius(obj.borderRadius);
|
|
const clipFunc = createRoundedRectClipFunc(width, height, cornerRadius);
|
|
|
|
const dragBoundFunc =
|
|
draggable && hasStageBounds
|
|
? (pos: { x: number; y: number }) =>
|
|
clampPositionToStage(
|
|
pos.x,
|
|
pos.y,
|
|
width,
|
|
height,
|
|
stageWidth!,
|
|
stageHeight!,
|
|
)
|
|
: undefined;
|
|
|
|
const handleDragEnd = (e: Konva.KonvaEventObject<DragEvent>) => {
|
|
const node = e.target;
|
|
let x = node.x();
|
|
let y = node.y();
|
|
if (hasStageBounds) {
|
|
({ x, y } = clampPositionToStage(
|
|
x,
|
|
y,
|
|
width,
|
|
height,
|
|
stageWidth!,
|
|
stageHeight!,
|
|
));
|
|
node.position({ x, y });
|
|
}
|
|
onUpdate(obj.id, { x, y });
|
|
};
|
|
|
|
const handleClick = (e: Konva.KonvaEventObject<MouseEvent>) => {
|
|
const node = cornerRadius > 0 ? groupRef.current : shapeRef.current;
|
|
if (node) {
|
|
onSelect(obj.id, node, e);
|
|
}
|
|
};
|
|
|
|
if (cornerRadius > 0) {
|
|
return (
|
|
<Group
|
|
ref={groupRef}
|
|
id={obj.id}
|
|
name="canvas-object"
|
|
x={obj.x}
|
|
y={obj.y}
|
|
rotation={obj.rotation || 0}
|
|
draggable={draggable}
|
|
clipFunc={clipFunc}
|
|
dragBoundFunc={dragBoundFunc}
|
|
onClick={handleClick}
|
|
onDragEnd={handleDragEnd}
|
|
>
|
|
<KonvaImage
|
|
x={0}
|
|
y={0}
|
|
image={image}
|
|
width={width}
|
|
height={height}
|
|
/>
|
|
{isSelected && (
|
|
<Rect
|
|
x={0}
|
|
y={0}
|
|
width={width}
|
|
height={height}
|
|
stroke="#3b82f6"
|
|
strokeWidth={3}
|
|
cornerRadius={cornerRadius}
|
|
listening={false}
|
|
/>
|
|
)}
|
|
</Group>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<KonvaImage
|
|
ref={shapeRef}
|
|
id={obj.id}
|
|
name="canvas-object"
|
|
x={obj.x}
|
|
y={obj.y}
|
|
image={image}
|
|
width={width}
|
|
height={height}
|
|
stroke={isSelected ? "#3b82f6" : undefined}
|
|
strokeWidth={isSelected ? 3 : 0}
|
|
rotation={obj.rotation || 0}
|
|
draggable={draggable}
|
|
dragBoundFunc={dragBoundFunc}
|
|
onClick={handleClick}
|
|
onDragEnd={handleDragEnd}
|
|
/>
|
|
);
|
|
};
|
|
|
|
export default ImageObject;
|