fix: RouteLine 站点三修——接点从黑影外缘改回卡描边(node.right 对齐真实卡边),线层 z-10 置卡上(点不再被卡遮挡),圆心骑描边各半在卡;E2E 同步断言
No files matched your search
@@ -115,7 +115,7 @@ test.describe('RouteLine 规格契约', () => {
|
|||||||
expect(specs).toContain('4 2')
|
expect(specs).toContain('4 2')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('站点 r6 半嵌:源骑卡右黑影外缘、目标贴左描边', async ({ page }) => {
|
test('站点 r6 半嵌:源/目标圆心都骑卡描边(各半在卡上),线层在卡之上', async ({ page }) => {
|
||||||
const c1 = await page.locator('a[href="#ch1"]').first().boundingBox()
|
const c1 = await page.locator('a[href="#ch1"]').first().boundingBox()
|
||||||
const c2 = await page.locator('a[href="#ch2"]').first().boundingBox()
|
const c2 = await page.locator('a[href="#ch2"]').first().boundingBox()
|
||||||
const wrap = await page.locator('main div.relative').first().boundingBox()
|
const wrap = await page.locator('main div.relative').first().boundingBox()
|
||||||
@@ -126,10 +126,17 @@ test.describe('RouteLine 规格契约', () => {
|
|||||||
)
|
)
|
||||||
expect(stations.length).toBeGreaterThanOrEqual(4)
|
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
|
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 - c1Right) <= 3), `源站点圆心应骑卡右描边 x≈${c1Right}`).toBeTruthy()
|
||||||
expect(stations.some((s) => Math.abs(s.cx - c2Border) <= 3), `目标站点应贴左描边 x≈${c2Border}`).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 }) => {
|
test('实线端点落在水平端口(M x y H 起、H x 止)', async ({ page }) => {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 135 KiB |
@@ -1,14 +1,15 @@
|
|||||||
/** RouteLine —— 关系线:线形即含义(实=先后 虚=相关 点=同族)
|
/** RouteLine —— 关系线:线形即含义(实=先后 虚=相关 点=同族)
|
||||||
* 拓扑:横平竖直 + R8 直角步进;并行不合并(4px);站点 r6 半嵌卡边
|
* 拓扑:横平竖直 + R8 直角步进;并行不合并(4px);站点 r6 半嵌卡边
|
||||||
* (左贴描边、右骑硬投影外缘);同口多线自动聚合胶囊(线止于胶囊边界)。 */
|
* (圆心骑左右描边,各半在卡上);线层 z-10 置于卡之上,站点不被卡遮挡;
|
||||||
|
* 同口多线自动聚合胶囊(线止于胶囊边界)。 */
|
||||||
import type { ChapterId, Edge } from '../lib/types'
|
import type { ChapterId, Edge } from '../lib/types'
|
||||||
|
|
||||||
const STATION_COLORS = ['var(--color-clash2a)', 'var(--color-clash1b)', 'var(--color-clash3a)', 'var(--color-clash1a)']
|
const STATION_COLORS = ['var(--color-clash2a)', 'var(--color-clash1b)', 'var(--color-clash3a)', 'var(--color-clash1a)']
|
||||||
|
|
||||||
export interface LaneNode {
|
export interface LaneNode {
|
||||||
id: ChapterId
|
id: ChapterId
|
||||||
borderLeft: number // 左描边 x(无影侧,入口贴这里)
|
borderLeft: number // 左描边 x(入口圆心贴这里,点半嵌)
|
||||||
right: number // 右侧黑影外缘 x(描边 + 7,出口从这里出发)
|
right: number // 右描边 x(出口圆心贴这里,点半嵌——不接阴影)
|
||||||
y: number // 卡中线 y
|
y: number // 卡中线 y
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ export function RouteLine({
|
|||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
viewBox={`0 0 ${width} ${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
|
aria-hidden
|
||||||
>
|
>
|
||||||
{edges.map((e, i) => {
|
{edges.map((e, i) => {
|
||||||
@@ -65,7 +66,7 @@ export function RouteLine({
|
|||||||
if (!a || !b) return null
|
if (!a || !b) return null
|
||||||
const ins = insByTo.get(e.to)!
|
const ins = insByTo.get(e.to)!
|
||||||
const capsule = ins.length > 1 && firstIn.has(e.to)
|
const capsule = ins.length > 1 && firstIn.has(e.to)
|
||||||
// 源站点骑出边卡黑影外缘;目标:单线=圆点贴描边,多线=胶囊(只画一次)
|
// 源/目标站点:圆心骑卡描边,各半在卡上;目标:单线=圆点,多线=胶囊(只画一次)
|
||||||
return (
|
return (
|
||||||
<g key={`st-${e.from}-${e.to}`}>
|
<g key={`st-${e.from}-${e.to}`}>
|
||||||
<circle cx={a.right} cy={a.y} r={6} fill={stationColor(si++)} stroke="var(--color-ink)" strokeWidth={2} />
|
<circle cx={a.right} cy={a.y} r={6} fill={stationColor(si++)} stroke="var(--color-ink)" strokeWidth={2} />
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ import { RouteLine } from '../../components/RouteLine'
|
|||||||
import { AssignBlock, Ticket } from '../../components/Ticket'
|
import { AssignBlock, Ticket } from '../../components/Ticket'
|
||||||
import { EntryGuide } from '../../components/EntryGuide'
|
import { EntryGuide } from '../../components/EntryGuide'
|
||||||
|
|
||||||
|
/* 节点=卡描边真实边缘:卡1(40,72,250) 卡2(380,0,250) 卡3(700,0,150) */
|
||||||
const nodes = {
|
const nodes = {
|
||||||
1: { id: 1, borderLeft: 40, right: 299, y: 180 },
|
1: { id: 1, borderLeft: 40, right: 290, y: 180 },
|
||||||
2: { id: 2, borderLeft: 378, right: 639, y: 108 },
|
2: { id: 2, borderLeft: 380, right: 630, y: 108 },
|
||||||
5: { id: 5, borderLeft: 699, right: 848, y: 108 },
|
5: { id: 5, borderLeft: 700, right: 850, y: 108 },
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import { EntryGuide } from '../../components/EntryGuide'
|
|||||||
import { Scatter } from '../../components/Scatter'
|
import { Scatter } from '../../components/Scatter'
|
||||||
import { Bar, Cross, Ring, Tremble, Tri, Wave } from '../../components/shapes'
|
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 = {
|
const nodes = {
|
||||||
1: { id: 1, borderLeft: 40, right: 299, y: 180 },
|
1: { id: 1, borderLeft: 40, right: 290, y: 180 },
|
||||||
2: { id: 2, borderLeft: 378, right: 639, y: 108 },
|
2: { id: 2, borderLeft: 380, right: 630, y: 108 },
|
||||||
5: { id: 5, borderLeft: 698, right: 848, y: 108 },
|
5: { id: 5, borderLeft: 700, right: 840, y: 108 },
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 135 KiB |