ERP-node/frontend/components/screen/AnimatedFlowEdge.tsx

71 lines
1.9 KiB
TypeScript

"use client";
import React from "react";
import { BaseEdge, getBezierPath, type EdgeProps } from "@xyflow/react";
// 커스텀 애니메이션 엣지 — bezier 곡선 + 흐르는 파티클 + 글로우 레이어
export function AnimatedFlowEdge({
id,
sourceX,
sourceY,
targetX,
targetY,
sourcePosition,
targetPosition,
style,
markerEnd,
data,
}: EdgeProps) {
const [edgePath] = getBezierPath({
sourceX,
sourceY,
sourcePosition,
targetX,
targetY,
targetPosition,
});
const strokeColor = (style?.stroke as string) || "hsl(var(--primary))";
const strokeW = (style?.strokeWidth as number) || 2;
const isActive = data?.active !== false;
const duration = data?.duration || "3s";
const filterId = `edge-glow-${id}`;
return (
<>
{/* 글로우용 SVG 필터 정의 (엣지별 고유 ID) */}
<defs>
<filter id={filterId} x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
{/* 글로우 레이어 */}
<path
d={edgePath}
fill="none"
stroke={strokeColor}
strokeWidth={strokeW + 4}
strokeOpacity={0.12}
filter={`url(#${filterId})`}
/>
{/* 메인 엣지 */}
<BaseEdge id={id} path={edgePath} style={style} markerEnd={markerEnd} />
{/* 흐르는 파티클 */}
{isActive && (
<>
<circle r="3" fill={strokeColor} filter={`url(#${filterId})`}>
<animateMotion dur={duration} repeatCount="indefinite" path={edgePath} />
</circle>
<circle r="1.5" fill="white" opacity="0.85">
<animateMotion dur={duration} repeatCount="indefinite" path={edgePath} />
</circle>
</>
)}
</>
);
}