Politur: Performance-Bottleneck (ctx.measureText) durch clientseitiges Text-Breiten-Caching auf dem Knoten geloest
This commit is contained in:
@@ -61,7 +61,7 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
||||
}
|
||||
}, [data])
|
||||
|
||||
// Grad (Degree) berechnen – fängt D3-Objektmutationen robust ab!
|
||||
// Grad (Degree) berechnen – fängt D3-Objektmutationen robust ab
|
||||
const degree = useMemo(() => {
|
||||
const d: Record<string, number> = {}
|
||||
data.edges.forEach((e) => {
|
||||
@@ -75,7 +75,7 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
||||
return d
|
||||
}, [data])
|
||||
|
||||
// Quadranten-Fokuskoordinaten für sauberes Clustering (Fokuszentren weiter auseinander gezogen)
|
||||
// Quadranten-Fokuskoordinaten für sauberes Clustering
|
||||
const getClusterX = (category: string) => {
|
||||
return {
|
||||
identity: -240,
|
||||
@@ -132,7 +132,7 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
||||
fgRef.current.zoomToFit(400, 40)
|
||||
}
|
||||
|
||||
// Nachbarmenge für extrem schnellen Lookup – fängt D3-Objektmutationen robust ab!
|
||||
// Nachbarmenge für extrem schnellen Lookup – fängt D3-Objektmutationen robust ab
|
||||
const connectedNodes = useMemo(() => {
|
||||
if (!activeNode) return new Set<string>()
|
||||
const set = new Set<string>()
|
||||
@@ -157,15 +157,11 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
||||
backgroundColor="rgba(0,0,0,0)"
|
||||
cooldownTicks={120}
|
||||
|
||||
// Exakte Hit-Map auf unsichtbarem Canvas zeichnen – fängt D3-Objektmutationen robust ab!
|
||||
// Ultraschneller, statischer Klick- & Hover-Puffer auf dem Hit-Canvas (verhindert teure Echtzeitrechnungen)
|
||||
nodePointerAreaPaint={(node: any, color: string, ctx: CanvasRenderingContext2D) => {
|
||||
const x = node.x ?? 0
|
||||
const y = node.y ?? 0
|
||||
const nId = typeof node === "object" ? node.id : node
|
||||
const size = 6 + Math.min(degree[nId] || 0, 15) * 0.8
|
||||
ctx.fillStyle = color
|
||||
ctx.beginPath()
|
||||
ctx.arc(x, y, size + 5, 0, 2 * Math.PI, false) // 5px Puffer für einfaches Treffen
|
||||
ctx.arc(node.x ?? 0, node.y ?? 0, 16, 0, 2 * Math.PI, false)
|
||||
ctx.fill()
|
||||
}}
|
||||
|
||||
@@ -239,16 +235,26 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
||||
}
|
||||
|
||||
// 2. Zeichne das Label nur unter bestimmten Bedingungen (Zoom-abhängig)
|
||||
// Verhindert Text-Clutter und macht Knotenpunkte permanent sichtbar.
|
||||
const showLabel = isHovered || isSelected || (globalScale >= 1.25) || (activeNode && connectedNodes.has(node.id))
|
||||
|
||||
if (showLabel) {
|
||||
const label = node.content.length > 35 ? node.content.slice(0, 34) + "…" : node.content
|
||||
|
||||
// Textbreite EINMALIG auf dem Objekt cachen (verhindert massive Frame-Einbrüche durch ctx.measureText)
|
||||
if (node.__baseTextWidth === undefined) {
|
||||
const prevFont = ctx.font
|
||||
ctx.font = '500 10px "Space Grotesk", sans-serif'
|
||||
node.__baseTextWidth = ctx.measureText(label).width
|
||||
ctx.font = prevFont
|
||||
}
|
||||
|
||||
const baseFontSize = isHovered || isSelected ? 10.5 : 9.5
|
||||
const fontSize = baseFontSize / Math.max(0.5, globalScale)
|
||||
|
||||
ctx.font = `${isHovered || isSelected ? "bold" : "500"} ${fontSize}px "Space Grotesk", sans-serif`
|
||||
const textWidth = ctx.measureText(label).width
|
||||
|
||||
// Breite linear zur geänderten FontSize skalieren – KEIN ctx.measureText Aufruf mehr pro Frame!
|
||||
const textWidth = node.__baseTextWidth * (fontSize / 10)
|
||||
const bpad = 4.5 / Math.max(0.5, globalScale)
|
||||
|
||||
ctx.fillStyle = isDimmed ? "rgba(10, 15, 25, 0.45)" : "rgba(8, 12, 20, 0.88)"
|
||||
|
||||
Reference in New Issue
Block a user