Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 92 additions & 46 deletions src/designs/structures/sequence-interaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
<Path
d={`M ${centerX} ${startY} L ${centerX} ${endY}`}
stroke={colorBorder}
strokeWidth={lifelineWidth}
strokeDasharray="5,5"
fill="none"
data-element-type="shape"
/>,
);

// 绘制生命线末端箭头(实心)
decorElements.push(
...createArrowElements(
centerX,
endY,
Math.PI / 2,
'triangle',
colorBorder,
1,
10,
),
);
});
}

// 绘制泳道标题
if (showLaneHeader) {
lanes.forEach((lane, laneIndex) => {
Expand Down Expand Up @@ -523,19 +491,6 @@ export const SequenceInteractionFlow: ComponentType<
options,
);

// 添加节点背景遮挡层,防止生命线虚线透过半透明节点显示
// 只在节点中心放置窄条遮挡生命线,避免圆角处露出白色背景
const maskStripWidth = lifelineWidth + 6;
decorElements.push(
<Rect
x={centerX - maskStripWidth / 2}
y={y}
width={maskStripWidth}
height={itemHeight}
fill={colorBg}
/>,
);

// 构造类似 hierarchy-tree 的 _originalIndex
const originalIndex = [laneIndex, rowIndex];
// 附加到数据上,确保 Item 组件能正确识别
Expand Down Expand Up @@ -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(
<mask
id={maskId}
maskUnits="userSpaceOnUse"
x={0}
y={0}
width={totalWidth}
height={totalHeight}
>
{/* 白色底:显示所有线条 */}
<Rect
x={0}
y={0}
width={totalWidth}
height={totalHeight}
fill="white"
/>
{/* 黑色块:在节点位置挖空生命线,上下各留 LIFELINE_MASK_GAP 间距 */}
{laneNodeRects.map((rect) => (
<Rect
x={rect.x}
y={rect.y - LIFELINE_MASK_GAP}
width={rect.width}
height={rect.height + LIFELINE_MASK_GAP * 2}
fill="black"
/>
))}
</mask>,
);
lifelineMaskAttr = `url(#${maskId})`;
}

decorElements.push(
<Path
d={`M ${centerX} ${startY} L ${centerX} ${endY}`}
stroke={colorBorder}
strokeWidth={lifelineWidth}
strokeDasharray="5,5"
fill="none"
data-element-type="shape"
mask={lifelineMaskAttr}
/>,
);

// 绘制生命线末端箭头(实心)
decorElements.push(
...createArrowElements(
centerX,
endY,
Math.PI / 2,
'triangle',
colorBorder,
1,
10,
),
);
});
}

// 添加新泳道按钮 (最右侧)
const lastLaneRightX = getLaneCenterX(lanes.length - 1) + laneWidth / 2;
const newLaneX =
Expand Down