diff --git a/src/components/TreeFlow.vue b/src/components/TreeFlow.vue index 7a24b1a..6169edd 100644 --- a/src/components/TreeFlow.vue +++ b/src/components/TreeFlow.vue @@ -184,33 +184,36 @@ onMounted(() => { function edgePath(e: FlowEdge): string { if (e.points.length < 2) return '' - const r = 6 - let d = `M ${e.points[0].x} ${e.points[0].y}` + const pts = e.points.map(p => ({ h: p.x, v: p.y })) + return catmullRomPath(pts) +} - for (let i = 1; i < e.points.length - 1; i++) { - const prev = e.points[i - 1] - const curr = e.points[i] - const next = e.points[i + 1] +function catmullRomPath(pts: { h: number; v: number }[]): string { + if (pts.length < 2) return '' + let d = `M ${pts[0].h} ${pts[0].v}` - const dx1 = curr.x - prev.x - const dy1 = curr.y - prev.y - const len1 = Math.sqrt(dx1 * dx1 + dy1 * dy1) || 1 - - const dx2 = next.x - curr.x - const dy2 = next.y - curr.y - const len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1 - - const rx = Math.min(r, len1 / 2, len2 / 2) - const ax = curr.x - (dx1 / len1) * rx - const ay = curr.y - (dy1 / len1) * rx - const bx = curr.x + (dx2 / len2) * rx - const by = curr.y + (dy2 / len2) * rx - - d += ` L ${ax} ${ay} Q ${curr.x} ${curr.y} ${bx} ${by}` + if (pts.length === 2) { + d += ` L ${pts[1].h} ${pts[1].v}` + return d } - const last = e.points[e.points.length - 1] - d += ` L ${last.x} ${last.y}` + for (let i = 0; i < pts.length - 1; i++) { + const p0 = pts[Math.max(0, i - 1)] + const p1 = pts[i] + const p2 = pts[i + 1] + const p3 = pts[Math.min(pts.length - 1, i + 2)] + + const cp1x = p1.h + (p2.h - p0.h) / 6 + const cp1y = p1.v + (p2.v - p0.v) / 6 + const cp2x = p2.h - (p3.h - p1.h) / 6 + const cp2y = p2.v - (p3.v - p1.v) / 6 + + if (i === 0) { + d += ` C ${cp1x} ${cp1y} ${cp2x} ${cp2y} ${p2.h} ${p2.v}` + } else { + d += ` S ${cp2x} ${cp2y} ${p2.h} ${p2.v}` + } + } return d } @@ -228,17 +231,59 @@ const svgH = computed(() => containerH.value) :width="svgW" :height="svgH" > + + + + + + + + + + + + + + + + + + containerH.value) const last = pts[pts.length - 1] const prev = pts[pts.length - 2] const angle = Math.atan2(last.y - prev.y, last.x - prev.x) - const s = 5 + const s = 6 return [ `${last.x + Math.cos(angle) * s} ${last.y + Math.sin(angle) * s}`, `${last.x + Math.cos(angle + 2.5) * s} ${last.y + Math.sin(angle + 2.5) * s}`, `${last.x + Math.cos(angle - 2.5) * s} ${last.y + Math.sin(angle - 2.5) * s}`, ].join(' ') })()" - :fill="edge.visited ? '#c9a84c' : '#333'" + :fill="edge.visited ? '#e0c060' : '#333'" + :filter="edge.visited ? 'url(#edge-glow)' : ''" /> @@ -404,7 +450,16 @@ const svgH = computed(() => containerH.value) color: #444; } -.flow-edge-group { - opacity: 0.6; +@keyframes flowDash { + to { stroke-dashoffset: -23; } +} + +.edge-main { + stroke-linecap: round; +} + +.edge-flow { + stroke-linecap: round; + animation: flowDash 3s linear infinite; }