Draw canvas connections in screen overlay

This commit is contained in:
Codex
2026-05-11 22:20:11 +08:00
parent 5cbc0d213d
commit eb7e5fd97e

View File

@@ -11,8 +11,8 @@ import {
Position,
ReactFlow,
ReactFlowProvider,
ViewportPortal,
useReactFlow,
useViewport,
type Connection,
type Edge,
type EdgeChange,
@@ -725,11 +725,26 @@ function clampViewport(viewport: FlowViewport, minZoom: number, maxZoom: number)
};
}
function buildConnectionPath(source: CanvasNode, target: CanvasNode) {
const sourceX = source.x + source.width + 8;
const sourceY = source.y + source.height / 2;
const targetX = target.x - 8;
const targetY = target.y + target.height / 2;
function flowToScreen(point: { x: number; y: number }, viewport: CanvasViewport) {
return {
x: point.x * viewport.zoom + viewport.x,
y: point.y * viewport.zoom + viewport.y,
};
}
function buildConnectionPath(source: CanvasNode, target: CanvasNode, viewport: CanvasViewport) {
const sourcePoint = flowToScreen({
x: source.x + source.width + 8,
y: source.y + source.height / 2,
}, viewport);
const targetPoint = flowToScreen({
x: target.x - 8,
y: target.y + target.height / 2,
}, viewport);
const sourceX = sourcePoint.x;
const sourceY = sourcePoint.y;
const targetX = targetPoint.x;
const targetY = targetPoint.y;
const distance = Math.max(80, Math.abs(targetX - sourceX) * 0.45);
return `M ${sourceX} ${sourceY} C ${sourceX + distance} ${sourceY}, ${targetX - distance} ${targetY}, ${targetX} ${targetY}`;
}
@@ -745,6 +760,7 @@ function CanvasConnectionOverlay({
edgeStroke: string;
edgeZIndex: number;
}) {
const viewport = useViewport();
const nodeMap = useMemo(() => new Map(nodes.map(node => [node.id, node])), [nodes]);
const visibleConnections = connections
.map(connection => {
@@ -757,49 +773,46 @@ function CanvasConnectionOverlay({
if (visibleConnections.length === 0) return null;
return (
<ViewportPortal>
<svg
className="canvas-connection-overlay"
style={{
position: 'absolute',
left: 0,
top: 0,
width: 1,
height: 1,
overflow: 'visible',
pointerEvents: 'none',
zIndex: edgeZIndex,
}}
aria-hidden="true"
>
<defs>
<marker
id="canvas-connection-arrow"
markerWidth="12"
markerHeight="12"
refX="10"
refY="6"
orient="auto"
markerUnits="strokeWidth"
>
<path d="M 2 2 L 10 6 L 2 10 z" fill={edgeStroke} />
</marker>
</defs>
{visibleConnections.map(({ connection, source, target }) => (
<path
key={connection.id}
d={buildConnectionPath(source, target)}
fill="none"
stroke={edgeStroke}
strokeWidth={3}
strokeLinecap="round"
strokeLinejoin="round"
markerEnd="url(#canvas-connection-arrow)"
opacity={0.95}
/>
))}
</svg>
</ViewportPortal>
<svg
className="canvas-connection-overlay"
style={{
position: 'absolute',
inset: 0,
width: '100%',
height: '100%',
overflow: 'visible',
pointerEvents: 'none',
zIndex: edgeZIndex,
}}
aria-hidden="true"
>
<defs>
<marker
id="canvas-connection-arrow"
markerWidth="12"
markerHeight="12"
refX="10"
refY="6"
orient="auto"
markerUnits="strokeWidth"
>
<path d="M 2 2 L 10 6 L 2 10 z" fill={edgeStroke} />
</marker>
</defs>
{visibleConnections.map(({ connection, source, target }) => (
<path
key={connection.id}
d={buildConnectionPath(source, target, viewport)}
fill="none"
stroke={edgeStroke}
strokeWidth={3}
strokeLinecap="round"
strokeLinejoin="round"
markerEnd="url(#canvas-connection-arrow)"
opacity={0.95}
/>
))}
</svg>
);
}