import { IPalmistryFinger, IPalmistryLine, IPalmistryPoint, } from "@/api/resources/Palmistry"; import "./scanned-photo.css"; import { useCallback, useEffect, useRef, useState } from "react"; type Props = { photo: string; small: boolean; displayLines: boolean; lines: Array; fingers: IPalmistryFinger[]; drawElementChangeDelay: number; startDelay: number; drawElements: Array; className?: string; isDecorationShown?: boolean; lineLabelsShown?: boolean; }; export default function StepScanPhoto(props: Props) { const { className: classNameProp = "", isDecorationShown = true } = props; const className = ["scanned-photo", classNameProp]; const { lines, drawElementChangeDelay, fingers, drawElements, lineLabelsShown = false } = props; const imageRef = useRef(null); const linesRef = useRef([]); const [isImageLoaded, setIsImageLoaded] = useState(false); const [imageWidth, setImageWidth] = useState(0); const [imageHeight, setImageHeight] = useState(0); const [textPositions, setTextPositions] = useState>([]); if (props.small) { className.push("scanned-photo_small"); } useEffect(() => { if (isImageLoaded && imageRef.current) { setImageWidth(imageRef.current.width || 0); setImageHeight(imageRef.current.height || 0); } }, [isImageLoaded]); useEffect(() => { if (!imageWidth || !imageHeight || !lines.length) return; const textWidth = 90; const textHeight = 17; const padding = 10; const newPositions: Array<{ x: number, y: number }> = []; lines.forEach((line, index) => { const points = line.points; const positions = []; for (let i = 0; i < points.length - 1; i++) { const x = (points[i].x + points[i + 1].x) / 2; const y = (points[i].y + points[i + 1].y) / 2; positions.push({ x, y }); } positions.unshift({ x: points[0].x, y: points[0].y }); positions.push({ x: points[points.length - 1].x, y: points[points.length - 1].y }); let positionFound = false; for (const pos of positions) { let hasOverlap = false; for (const existingPos of newPositions) { if ( pos.x * imageWidth + padding < existingPos.x + textWidth && pos.x * imageWidth + padding + textWidth > existingPos.x && pos.y * imageHeight - padding < existingPos.y + textHeight && pos.y * imageHeight - padding + textHeight > existingPos.y ) { hasOverlap = true; break; } } if (!hasOverlap) { newPositions.push({ x: pos.x * imageWidth + 10, y: pos.y * imageHeight - 5 }); positionFound = true; break; } } if (!positionFound) { newPositions.push({ x: points[0].x * imageWidth + textWidth + padding * (index + 1), y: points[0].y * imageHeight - textHeight - padding * (index + 1) }); } }); setTextPositions(newPositions); }, [lines, imageWidth, imageHeight]); const getCoordinatesString = useCallback( (points: IPalmistryPoint[]) => { const coordinatesString = `M ${points[0]?.x * imageWidth} ${points[0]?.y * imageHeight }`; return points.reduce( (acc, point) => `${acc} L ${point?.x * imageWidth} ${point?.y * imageHeight}`, coordinatesString ); }, // eslint-disable-next-line react-hooks/exhaustive-deps [lines, isImageLoaded, imageWidth, imageHeight] ); const getLineLength = (line: SVGPathElement) => { return line?.getTotalLength(); }; return (
PalmIcon setIsImageLoaded(true)} // width={imageWidth} // height={imageHeight} />
{!!imageHeight && !!imageWidth && ( {!!fingers.length && fingers?.map((finger, index) => { return ( ); })} {props.displayLines && ( <> {lines.map((line, index) => ( (linesRef.current[index] = el as SVGPathElement) } style={{ strokeDasharray: getLineLength(linesRef.current[index]) || 500, strokeDashoffset: getLineLength(linesRef.current[index]) || 500, animationDelay: `${drawElementChangeDelay * (index + 1) }ms`, }} /> ))} {lineLabelsShown && lines.map((line, index) => ( {line.label || line.name} ))} )} )}
); }