diff --git a/apps/design-lab/e2e/pages.spec.ts b/apps/design-lab/e2e/pages.spec.ts index f478fa9..041c8a4 100644 --- a/apps/design-lab/e2e/pages.spec.ts +++ b/apps/design-lab/e2e/pages.spec.ts @@ -115,7 +115,7 @@ test.describe('RouteLine 规格契约', () => { expect(specs).toContain('4 2') }) - test('站点 r6 半嵌:源骑卡右黑影外缘、目标贴左描边', async ({ page }) => { + test('站点 r6 半嵌:源/目标圆心都骑卡描边(各半在卡上),线层在卡之上', async ({ page }) => { const c1 = await page.locator('a[href="#ch1"]').first().boundingBox() const c2 = await page.locator('a[href="#ch2"]').first().boundingBox() const wrap = await page.locator('main div.relative').first().boundingBox() @@ -126,10 +126,17 @@ test.describe('RouteLine 规格契约', () => { ) expect(stations.length).toBeGreaterThanOrEqual(4) - const c1ShadowEdge = c1!.x - wrap!.x + c1!.width + 7 // 描边外 + 7px 黑影 + const c1Right = c1!.x - wrap!.x + c1!.width // 卡右描边(不接阴影) const c2Border = c2!.x - wrap!.x - expect(stations.some((s) => Math.abs(s.cx - c1ShadowEdge) <= 3), `源站点应骑黑影外缘 x≈${c1ShadowEdge}`).toBeTruthy() - expect(stations.some((s) => Math.abs(s.cx - c2Border) <= 3), `目标站点应贴左描边 x≈${c2Border}`).toBeTruthy() + expect(stations.some((s) => Math.abs(s.cx - c1Right) <= 3), `源站点圆心应骑卡右描边 x≈${c1Right}`).toBeTruthy() + expect(stations.some((s) => Math.abs(s.cx - c2Border) <= 3), `目标站点圆心应骑左描边 x≈${c2Border}`).toBeTruthy() + + // 线层必须置于卡之上——否则半嵌的站点被卡盖住,只剩卡外一小块 + const z = await page.evaluate(() => { + const svg = document.querySelector('main div.relative > svg') as SVGElement | null + return svg ? getComputedStyle(svg).zIndex : 'missing' + }) + expect(z).toBe('10') }) test('实线端点落在水平端口(M x y H 起、H x 止)', async ({ page }) => { diff --git a/apps/design-lab/e2e/visual.spec.ts-snapshots/components-chromium-linux.png b/apps/design-lab/e2e/visual.spec.ts-snapshots/components-chromium-linux.png index 3325b8f..afed0f2 100644 Binary files a/apps/design-lab/e2e/visual.spec.ts-snapshots/components-chromium-linux.png and b/apps/design-lab/e2e/visual.spec.ts-snapshots/components-chromium-linux.png differ diff --git a/apps/design-lab/e2e/visual.spec.ts-snapshots/shelf-chromium-linux.png b/apps/design-lab/e2e/visual.spec.ts-snapshots/shelf-chromium-linux.png index f7fc434..0340b25 100644 Binary files a/apps/design-lab/e2e/visual.spec.ts-snapshots/shelf-chromium-linux.png and b/apps/design-lab/e2e/visual.spec.ts-snapshots/shelf-chromium-linux.png differ diff --git a/apps/design-lab/src/components/RouteLine.tsx b/apps/design-lab/src/components/RouteLine.tsx index f1a0734..f1f6313 100644 --- a/apps/design-lab/src/components/RouteLine.tsx +++ b/apps/design-lab/src/components/RouteLine.tsx @@ -1,14 +1,15 @@ /** RouteLine —— 关系线:线形即含义(实=先后 虚=相关 点=同族) * 拓扑:横平竖直 + R8 直角步进;并行不合并(4px);站点 r6 半嵌卡边 - * (左贴描边、右骑硬投影外缘);同口多线自动聚合胶囊(线止于胶囊边界)。 */ + * (圆心骑左右描边,各半在卡上);线层 z-10 置于卡之上,站点不被卡遮挡; + * 同口多线自动聚合胶囊(线止于胶囊边界)。 */ import type { ChapterId, Edge } from '../lib/types' const STATION_COLORS = ['var(--color-clash2a)', 'var(--color-clash1b)', 'var(--color-clash3a)', 'var(--color-clash1a)'] export interface LaneNode { id: ChapterId - borderLeft: number // 左描边 x(无影侧,入口贴这里) - right: number // 右侧黑影外缘 x(描边 + 7,出口从这里出发) + borderLeft: number // 左描边 x(入口圆心贴这里,点半嵌) + right: number // 右描边 x(出口圆心贴这里,点半嵌——不接阴影) y: number // 卡中线 y } @@ -47,7 +48,7 @@ export function RouteLine({ width={width} height={height} viewBox={`0 0 ${width} ${height}`} - className="pointer-events-none absolute left-0 top-0" + className="pointer-events-none absolute left-0 top-0 z-10" aria-hidden > {edges.map((e, i) => { @@ -65,7 +66,7 @@ export function RouteLine({ if (!a || !b) return null const ins = insByTo.get(e.to)! const capsule = ins.length > 1 && firstIn.has(e.to) - // 源站点骑出边卡黑影外缘;目标:单线=圆点贴描边,多线=胶囊(只画一次) + // 源/目标站点:圆心骑卡描边,各半在卡上;目标:单线=圆点,多线=胶囊(只画一次) return ( diff --git a/apps/design-lab/src/pages/components/App.tsx b/apps/design-lab/src/pages/components/App.tsx index 9e89cf5..0c9b046 100644 --- a/apps/design-lab/src/pages/components/App.tsx +++ b/apps/design-lab/src/pages/components/App.tsx @@ -5,10 +5,11 @@ import { RouteLine } from '../../components/RouteLine' import { AssignBlock, Ticket } from '../../components/Ticket' import { EntryGuide } from '../../components/EntryGuide' +/* 节点=卡描边真实边缘:卡1(40,72,250) 卡2(380,0,250) 卡3(700,0,150) */ const nodes = { - 1: { id: 1, borderLeft: 40, right: 299, y: 180 }, - 2: { id: 2, borderLeft: 378, right: 639, y: 108 }, - 5: { id: 5, borderLeft: 699, right: 848, y: 108 }, + 1: { id: 1, borderLeft: 40, right: 290, y: 180 }, + 2: { id: 2, borderLeft: 380, right: 630, y: 108 }, + 5: { id: 5, borderLeft: 700, right: 850, y: 108 }, } as const export function App() { diff --git a/apps/design-lab/src/pages/shelf/App.tsx b/apps/design-lab/src/pages/shelf/App.tsx index 2be34d1..ba585e4 100644 --- a/apps/design-lab/src/pages/shelf/App.tsx +++ b/apps/design-lab/src/pages/shelf/App.tsx @@ -9,11 +9,11 @@ import { EntryGuide } from '../../components/EntryGuide' import { Scatter } from '../../components/Scatter' import { Bar, Cross, Ring, Tremble, Tri, Wave } from '../../components/shapes' -/* 场景坐标(复刻已验收组合):卡1(40,110,250) 卡2(380,44,250,rot) 卡3(700,44,140) */ +/* 场景坐标(节点=卡描边真实边缘):卡1(40,110,250) 卡2(380,44,250,rot) 卡3(700,44,140) */ const nodes = { - 1: { id: 1, borderLeft: 40, right: 299, y: 180 }, - 2: { id: 2, borderLeft: 378, right: 639, y: 108 }, - 5: { id: 5, borderLeft: 698, right: 848, y: 108 }, + 1: { id: 1, borderLeft: 40, right: 290, y: 180 }, + 2: { id: 2, borderLeft: 380, right: 630, y: 108 }, + 5: { id: 5, borderLeft: 700, right: 840, y: 108 }, } as const export function App() { diff --git a/docs/screenshots/design-lab/pr_components.png b/docs/screenshots/design-lab/pr_components.png index 3325b8f..afed0f2 100644 Binary files a/docs/screenshots/design-lab/pr_components.png and b/docs/screenshots/design-lab/pr_components.png differ diff --git a/docs/screenshots/design-lab/pr_shelf.png b/docs/screenshots/design-lab/pr_shelf.png index f7fc434..0340b25 100644 Binary files a/docs/screenshots/design-lab/pr_shelf.png and b/docs/screenshots/design-lab/pr_shelf.png differ