工具提示与画布网络

3

我使用D3.v4和canvas制作了一个大型力导向图。由于尺寸过大,我不能使用SVG。

如何为画布上的元素制作工具提示?

至少,我该如何确定鼠标在画布上的位置?

我试图使用这个问题中的答案来检查鼠标光标在画布上的位置,然后使用simulation.find(x,y,radius)来识别附近的节点。然而,这返回了很多误报,我有一种感觉HTML事件和simulation.find()的工作方式和/或使用不同的坐标标准。

是否有人对此有任何想法或解决方案?

编辑:建议查看这个问题。我已经尝试过了,它有些起作用。现在的问题是,正如我所怀疑的那样,simulation.find(..)似乎不使用这些画布坐标来查找网络中的节点。

对于好奇的人,这是我用来查找光标和附近节点的当前函数:

canvas = d3.select('canvas')
function getPos(e) {
    rect = canvas.node().getBoundingClientRect();
    scaleX = width / rect.width;
    scaleY = height / rect.height;
    x = (e.clientX - rect.left);
    y = (e.clientY - rect.top);
    node = simulation.find(x,y, 3);
    return {
        x : x,
        y : y,
        node : node
    };
}

如果你将事件监听器添加到画布上,那么它只会在鼠标悬停在画布上时触发。 - Bálint
2
你读过这个问题吗? - Mike Cluck
是的,这正是预期的结果。我很好奇,event中的光标位置是否相对于触发事件的元素? - ThePenguin
1
没有,我没找到。谢谢 @MikeC,我会试一下的。 - ThePenguin
虽然那样做是可行的,但遗憾的是它并不能解决我的问题。我已经更新了问题。 - ThePenguin
1个回答

5

您应该使用内置的d3.mouse来确定画布内的位置:

d3.select("canvas").on("mousemove", function(d){
  var p = d3.mouse(this);
  var node = simulation.find(p[0], p[1]);
});

这是一个快速示例,突出显示鼠标位置最接近的节点:

<!DOCTYPE html>
<meta charset="utf-8">
<canvas width="960" height="600"></canvas>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
  var canvas = document.querySelector("canvas"),
    context = canvas.getContext("2d"),
    width = canvas.width,
    height = canvas.height;

  var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) {
      return d.id;
    }))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(width / 2, height / 2));


  var graph = {};
  graph.nodes = d3.range(100).map(function(d) {
    return {
      id: d
    }
  });
  graph.links = d3.range(200).map(function(d) {
    return {
      source: Math.floor(Math.random() * 100),
      target: Math.floor(Math.random() * 100)
    };
  });

  simulation
    .nodes(graph.nodes)
    .on("tick", ticked);

  simulation.force("link")
    .links(graph.links);

  function ticked() {
    context.clearRect(0, 0, width, height);

    context.beginPath();
    graph.links.forEach(drawLink);
    context.strokeStyle = "#aaa";
    context.stroke();

    context.beginPath();
    graph.nodes.forEach(drawNode);
    context.fill();
    context.strokeStyle = "#fff";
    context.stroke();
    
    if (closeNode) {
      context.beginPath();
      drawNode(closeNode)
      context.fill();
      context.strokeStyle = "#ff0000";
      context.stroke();
    }
  }
  
  var closeNode;
  d3.select("canvas").on("mousemove", function(d){
    var p = d3.mouse(this);
    closeNode = simulation.find(p[0], p[1]);
    ticked();
  })

  function drawLink(d) {
    context.moveTo(d.source.x, d.source.y);
    context.lineTo(d.target.x, d.target.y);
  }

  function drawNode(d) {
    context.moveTo(d.x + 3, d.y);
    context.arc(d.x, d.y, 3, 0, 2 * Math.PI);
  }
</script>


嘿,马克!我遇到了类似的问题。你的方法很完美。但是,当我缩放网络时,simulation.find()就不能再正确地检测节点了。我尝试通过这样做来获取当前缩放级别并更新d3.mouse(this)坐标:p[0]* currentZoom.k + currentZoom.x,但目前还没有成功。有什么建议吗? - user_jt
2
@user3204834,您可以使用transform.invert将点映射到变换空间。更新fiddle请点击这里 - Mark
哦,没错!我需要的是反向变换而不是新的变换。非常感谢你! - user_jt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接