Hover text: cross-fade on the same factor as the glow

The instant muted->bright color swap felt abrupt next to the fading
halo. The hover name is again drawn at partial alpha over the muted
resting label, using the SAME transition factor as the halo — text
and glow fade in/out in sync. Because every enter/leave interpolates
from the current fade value, rapid hover toggles at a node's hit
boundary glide instead of snapping (the flicker-damping fix).
This commit is contained in:
amosblomqvist
2026-09-02 02:34:08 +03:00
parent 96da582d35
commit 2d9c649f0b
+14 -9
View File
@@ -17,12 +17,13 @@
* - defaultDrawNodeHover: NO background box (sigma's default is a * - defaultDrawNodeHover: NO background box (sigma's default is a
* hardcoded white one). Instead the hovered node gets a soft glow * hardcoded white one). Instead the hovered node gets a soft glow
* halo in its own state color at a constant radius, flush with the * halo in its own state color at a constant radius, flush with the
* disc edge, and its name brightens instantly — the "this is * disc edge, and its name brightens — the "this is clickable,
* clickable, click for the full fact" affordance. Entering/leaving * click for the full fact" affordance. Entering/leaving hover fades
* hover fades the halo's OPACITY over a short transition (no * ONE transition factor shared by halo and text alike, so the
* growth, no pulse; the text color is un-animated so rapid hover * halo's opacity and the text's muted→bright cross-fade stay in
* toggles at a node's edge can't flicker it). Hover always shows * sync (no growth, no pulse; interpolating from the current value
* the name, even when labels are zoomed out. * means rapid hover toggles glide instead of snapping). Hover
* always shows the name, even when labels are zoomed out.
* Labels are zoom-gated GLOBALLY with a fade: all names appear at once * Labels are zoom-gated GLOBALLY with a fade: all names appear at once
* when the camera is at fit zoom or closer (ratio <= 1), then fade out * when the camera is at fit zoom or closer (ratio <= 1), then fade out
* together across a short zoom span before label rendering switches off — * together across a short zoom span before label rendering switches off —
@@ -145,9 +146,9 @@ function drawName(
* 1 = fully hovered): a glow halo in the node's own state color at a * 1 = fully hovered): a glow halo in the node's own state color at a
* CONSTANT radius (opacity fades in/out; the halo never grows) starting * CONSTANT radius (opacity fades in/out; the halo never grows) starting
* flush at the disc edge (the evenodd punch-out keeps the disc itself * flush at the disc edge (the evenodd punch-out keeps the disc itself
* unpainted). The name is drawn opaque in the bright hover color — no * unpainted). The name is drawn at partial alpha in the bright hover
* alpha animation: partial-alpha text over the resting label flickers * color — the muted resting label on the layer beneath shows through,
* when hover toggles rapidly at a node's edge. */ * so text and halo cross-fade on the SAME factor and stay in sync. */
function paintHover( function paintHover(
context: CanvasRenderingContext2D, context: CanvasRenderingContext2D,
data: { label?: string | null; x: number; y: number; size: number; color: string }, data: { label?: string | null; x: number; y: number; size: number; color: string },
@@ -168,6 +169,10 @@ function paintHover(
context.arc(data.x, data.y, inner, 0, Math.PI * 2, true); context.arc(data.x, data.y, inner, 0, Math.PI * 2, true);
context.fill("evenodd"); context.fill("evenodd");
} }
// Same fade factor as the halo: text and glow rise together. The
// bright name at partial alpha over the muted resting label beneath is
// a smooth muted→bright cross-fade rather than an instant color swap.
context.globalAlpha = fade;
drawName(context, data, settings, hoverTextColor); drawName(context, data, settings, hoverTextColor);
context.restore(); context.restore();
} }