02
Canvas 2D · Eased Highlight
노드 호버 하이라이트
노드에 마우스를 올려보세요 · 드래그로 회전
↑ 노드에 마우스를 올리면 연결된 그룹이 밝아집니다 · 드래그로 회전, 휠로 확대
마우스를 노드에 올리면 그 노드와 연결된 그룹만 스르륵 밝아지고 나머지는 어둡게 가라앉습니다. 핵심은 '이징(easing)' — 밝기를 즉시 바꾸지 않고 매 프레임 목표값으로 조금씩 수렴시켜, 켜짐/꺼짐이 부드럽게 흐릅니다.
- 커서 아래 가장 가까운 노드를 히트테스트로 찾고(반지름 + 여유 8px), 그 노드의 이웃 집합을 밝힘 대상으로 삼습니다.
- 각 노드의 밝기 cur 값을 목표(1 또는 0)로 EASE 비율만큼 매 프레임 수렴 — 어떤 값도 튀지 않습니다.
- 밝기에는 바닥값(DIM)을 둬서, 강조 안 된 노드도 완전히 사라지지 않고 은은히 남습니다.
node-hover.ts — 핵심 패턴 (distilled)
// 캔버스 노드 그래프의 "이징(easing) 호버 하이라이트".
// 매 프레임: 커서 아래 노드를 찾고, 모든 노드의 밝기를 목표값으로 부드럽게
// 수렴시킵니다 — 호버 노드와 그 이웃은 1로, 나머지는 어두운 바닥값으로.
// 딱 켜지고 꺼지는 게 아니라 스르륵 밝아졌다 사라집니다.
const EASE = 0.1; // 프레임당 blend 비율 (작을수록 더 부드럽게)
const DIM = 0.16; // 호버 안 된 노드의 최소 밝기(바닥값)
let hovered = null;
canvas.addEventListener("pointermove", (e) => {
const rect = canvas.getBoundingClientRect();
const px = e.clientX - rect.left;
const py = e.clientY - rect.top;
let best = null;
let bestD = Infinity;
for (const n of nodes) {
const dx = n.sx - px; // n.sx / n.sy = 노드의 화면 좌표
const dy = n.sy - py;
const d = dx * dx + dy * dy;
const hit = (n.radius + 8) ** 2; // 반지름 + 여유 8px
if (d < hit && d < bestD) { bestD = d; best = n; }
}
hovered = best;
});
function draw() {
for (const n of nodes) {
// 호버 노드거나 그 이웃이면 1, 아니면 0을 목표로
const lit =
!hovered || n === hovered || neighbours.get(hovered.id)?.has(n.id) ? 1 : 0;
n.cur += (lit - n.cur) * EASE; // 목표값으로 이징
const alpha = DIM + (1 - DIM) * n.cur; // 바닥값 → 최대 밝기
drawNode(n, alpha);
}
requestAnimationFrame(draw);
}