diff --git a/src/designs/structures/sequence-interaction.tsx b/src/designs/structures/sequence-interaction.tsx index 14f537d5..9150200d 100644 --- a/src/designs/structures/sequence-interaction.tsx +++ b/src/designs/structures/sequence-interaction.tsx @@ -100,6 +100,7 @@ const DEFAULT_ITEM_HEIGHT = 50; const FONT_SIZE = 14; const ARROW_SIZE = 14; const CORNER_RADIUS_NODE = 6; +const LIFELINE_MASK_GAP = 2; const LANE_PADDING = 60; const BTN_HALF_SIZE = 12; @@ -419,39 +420,6 @@ export const SequenceInteractionFlow: ComponentType< const defsElements: JSXElement[] = []; const btnElements: JSXElement[] = []; - // 绘制生命线 - if (showLifeline) { - lanes.forEach((_lane, laneIndex) => { - const centerX = getLaneCenterX(laneIndex); - const startY = padding + headerOffset; - const endY = totalHeight - padding; - - decorElements.push( - , - ); - - // 绘制生命线末端箭头(实心) - decorElements.push( - ...createArrowElements( - centerX, - endY, - Math.PI / 2, - 'triangle', - colorBorder, - 1, - 10, - ), - ); - }); - } - // 绘制泳道标题 if (showLaneHeader) { lanes.forEach((lane, laneIndex) => { @@ -523,19 +491,6 @@ export const SequenceInteractionFlow: ComponentType< options, ); - // 添加节点背景遮挡层,防止生命线虚线透过半透明节点显示 - // 只在节点中心放置窄条遮挡生命线,避免圆角处露出白色背景 - const maskStripWidth = lifelineWidth + 6; - decorElements.push( - , - ); - // 构造类似 hierarchy-tree 的 _originalIndex const originalIndex = [laneIndex, rowIndex]; // 附加到数据上,确保 Item 组件能正确识别 @@ -631,6 +586,97 @@ export const SequenceInteractionFlow: ComponentType< ); }); + // 绘制生命线(使用 mask 挖空节点区域,避免虚线穿透半透明节点) + if (showLifeline) { + // 预先按泳道分组节点,避免每条泳道都遍历全部节点 + const nodeRectsByLane = new Map< + number, + { x: number; y: number; width: number; height: number }[] + >(); + nodeLayoutById.forEach((layout) => { + let list = nodeRectsByLane.get(layout.laneIndex); + if (!list) { + list = []; + nodeRectsByLane.set(layout.laneIndex, list); + } + list.push({ + x: layout.x, + y: layout.y, + width: layout.width, + height: layout.height, + }); + }); + + lanes.forEach((_lane, laneIndex) => { + const centerX = getLaneCenterX(laneIndex); + const startY = padding + headerOffset; + const endY = totalHeight - padding; + + const laneNodeRects = nodeRectsByLane.get(laneIndex) ?? []; + + // 如果该泳道有节点,创建 mask 来挖空节点区域 + let lifelineMaskAttr: string | undefined; + if (laneNodeRects.length > 0) { + const maskId = `lifeline-mask-${instanceId}-${laneIndex}`; + defsElements.push( + + {/* 白色底:显示所有线条 */} + + {/* 黑色块:在节点位置挖空生命线,上下各留 LIFELINE_MASK_GAP 间距 */} + {laneNodeRects.map((rect) => ( + + ))} + , + ); + lifelineMaskAttr = `url(#${maskId})`; + } + + decorElements.push( + , + ); + + // 绘制生命线末端箭头(实心) + decorElements.push( + ...createArrowElements( + centerX, + endY, + Math.PI / 2, + 'triangle', + colorBorder, + 1, + 10, + ), + ); + }); + } + // 添加新泳道按钮 (最右侧) const lastLaneRightX = getLaneCenterX(lanes.length - 1) + laneWidth / 2; const newLaneX =