fix: 边线点击优化 — 双层线结构 + hover 高亮
- 每条边由底层粗透明线(12px)和上层可见线组成 - 粗透明线捕获点击,命中区域扩大10倍以上 - 线宽 2-6px(按通信频次),不透明度 0.45-0.80 - hover 时高亮:加粗+亮红+不透明 - tooltip 提示点击查看详情 - highlightNodeEdges 同步使用双层线 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1561,21 +1561,35 @@ function highlightNodeEdges() {
|
||||
const src = ipCoord[e.source];
|
||||
const dst = ipCoord[e.target];
|
||||
if (!src || !dst) return;
|
||||
const w = Math.min((e.comm_count || 1) / 3 + 1, 5);
|
||||
const poly = L.polyline([src, dst], {
|
||||
color: '#ff3333', weight: w, opacity: 0.75,
|
||||
interactive: true, bubblingMouseEvents: true,
|
||||
const w = Math.max(2, Math.min((e.comm_count || 1) / 3 + 1, 5));
|
||||
// 底层粗透明线用于点击捕获
|
||||
const hitPoly = L.polyline([src, dst], {
|
||||
color: '#ff3333', weight: 12, opacity: 0,
|
||||
interactive: true, bubblingMouseEvents: false,
|
||||
});
|
||||
poly.bindTooltip(
|
||||
// 上层细线用于展示
|
||||
const visPoly = L.polyline([src, dst], {
|
||||
color: '#ff3333', weight: w, opacity: 0.8,
|
||||
interactive: false, bubblingMouseEvents: true,
|
||||
});
|
||||
const pair = L.layerGroup([hitPoly, visPoly]);
|
||||
pair.bindTooltip(
|
||||
`${escapeHtml(e.source)} → ${escapeHtml(e.target)}<br>` +
|
||||
`${e.comm_count} 次 · ${(e.total_bytes/1024).toFixed(1)} KB`,
|
||||
{sticky: true}
|
||||
`${e.comm_count} 次 · ${(e.total_bytes/1024).toFixed(1)} KB` +
|
||||
`<br><span style="color:#aaa;">🖱 点击查看详情</span>`,
|
||||
{sticky: true, direction: 'top'}
|
||||
);
|
||||
poly.on('click', function(ev) {
|
||||
pair.on('click', function(ev) {
|
||||
L.DomEvent.stopPropagation(ev);
|
||||
loadEdgeDetail(e);
|
||||
});
|
||||
polys.push(poly);
|
||||
pair.on('mouseover', function() {
|
||||
visPoly.setStyle({ color: '#e60000', weight: Math.min(w + 2, 8), opacity: 1 });
|
||||
});
|
||||
pair.on('mouseout', function() {
|
||||
visPoly.setStyle({ color: '#ff3333', weight: w, opacity: 0.8 });
|
||||
});
|
||||
polys.push(pair);
|
||||
});
|
||||
|
||||
if (polys.length) {
|
||||
@@ -1594,7 +1608,16 @@ function highlightEdge(idx) {
|
||||
if (!_edgeLayer) return;
|
||||
const layers = _edgeLayer.getLayers();
|
||||
layers.forEach((l, i) => {
|
||||
l.setStyle({color: i === idx ? '#ff0000' : '#ff4444', weight: i === idx ? 4 : (l.options?.weight || 1), opacity: i === idx ? 1 : 0.5});
|
||||
// Each entry is a layerGroup containing [hitPoly, visPoly]
|
||||
const children = l.getLayers ? l.getLayers() : [];
|
||||
const visPoly = children.length >= 2 ? children[1] : children[0];
|
||||
if (visPoly && visPoly.setStyle) {
|
||||
visPoly.setStyle({
|
||||
color: i === idx ? '#e60000' : '#ff6666',
|
||||
weight: i === idx ? 6 : 2,
|
||||
opacity: i === idx ? 1 : 0.3
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1635,21 +1658,44 @@ function renderEdgesOnMap() {
|
||||
if (shown >= 300) return;
|
||||
shown++;
|
||||
|
||||
const weight = Math.min((e.comm_count || 1) / maxCount * 3 + 0.5, 4);
|
||||
const poly = L.polyline([srcCoord, dstCoord], {
|
||||
color: '#ff6b6b', weight: weight, opacity: 0.35,
|
||||
interactive: true, bubblingMouseEvents: true,
|
||||
// 线宽:最小2px,最大6px,按频次占比缩放
|
||||
const ratio = (e.comm_count || 1) / maxCount;
|
||||
const weight = Math.max(2, Math.min(ratio * 5 + 1.5, 6));
|
||||
const opacity = 0.45 + ratio * 0.35; // 0.45 ~ 0.80
|
||||
|
||||
// 底层的粗透明线 —— 用于点击捕获(不可见但可交互)
|
||||
const hitPoly = L.polyline([srcCoord, dstCoord], {
|
||||
color: '#ff6b6b', weight: 12, opacity: 0,
|
||||
interactive: true, bubblingMouseEvents: false,
|
||||
});
|
||||
poly.bindTooltip(
|
||||
// 上层的细线 —— 用于展示
|
||||
const visPoly = L.polyline([srcCoord, dstCoord], {
|
||||
color: '#ff6b6b', weight: weight, opacity: opacity,
|
||||
interactive: false, bubblingMouseEvents: true,
|
||||
});
|
||||
|
||||
// 将两条线绑定为一个组,共享 tooltip 和 click
|
||||
const pair = L.layerGroup([hitPoly, visPoly]);
|
||||
|
||||
pair.bindTooltip(
|
||||
`<b>${escapeHtml(e.source)} → ${escapeHtml(e.target)}</b><br>` +
|
||||
`${e.comm_count} 次 · ${e.total_bytes ? (e.total_bytes/1024).toFixed(1)+'KB' : ''}`,
|
||||
{sticky: true}
|
||||
`${e.comm_count} 次 · ${e.total_bytes ? (e.total_bytes/1024).toFixed(1)+'KB' : ''}` +
|
||||
`<br><span style="color:#aaa;">🖱 点击查看详情</span>`,
|
||||
{sticky: true, direction: 'top'}
|
||||
);
|
||||
poly.on('click', function(ev) {
|
||||
pair.on('click', function(ev) {
|
||||
L.DomEvent.stopPropagation(ev);
|
||||
loadEdgeDetail(e);
|
||||
});
|
||||
edgePolys.push(poly);
|
||||
// Hover 高亮
|
||||
pair.on('mouseover', function() {
|
||||
visPoly.setStyle({ color: '#e63946', weight: Math.min(weight + 2, 8), opacity: 0.95 });
|
||||
});
|
||||
pair.on('mouseout', function() {
|
||||
visPoly.setStyle({ color: '#ff6b6b', weight: weight, opacity: opacity });
|
||||
});
|
||||
|
||||
edgePolys.push(pair);
|
||||
});
|
||||
|
||||
if (edgePolys.length) _edgeLayer = L.layerGroup(edgePolys).addTo(SA.map);
|
||||
|
||||
Reference in New Issue
Block a user