【问题标题】:Show tool-tip on links of force directed graph in d3js在 d3js 中的力有向图链接上显示工具提示
【发布时间】:2015-10-16 07:54:34
【问题描述】:

我正在研究This 链接中给出的简单力有向图。它工作得很好,但我想在边缘上显示工具提示,就像它在节点上显示的那样。每当我在边缘移动鼠标时,它会在链接上显示一些工具提示(数据可能从 JSON 文件中检索)。 是否有内置方法,或者我必须在鼠标悬停时显示 div(在这种情况下如何获取鼠标的位置,即显示 div 的位置)

【问题讨论】:

标签: javascript d3.js force-layout


【解决方案1】:
  1. 有内置方法吗?

回答:是的。有一个内置的方式。大多数 HTML 元素都支持 title 属性。当您将鼠标指针移到该元素上时,会显示一个小工具提示一段时间或直到您离开该元素。

演示:

var w = 500,
  h = 200

var vis = d3.select("body").append("svg:svg")
  .attr("width", w)
  .attr("height", h);

var graph = {
  nodes: [{
    name: 'A'
  }, {
    name: 'B'
  }],
  links: [{
    source: 0,
    target: 1
  }]
};

var force = d3.layout.force()
  .nodes(graph.nodes)
  .links(graph.links)
  .gravity(.05)
  .distance(100)
  .charge(-100)
  .size([w, h])
  .start();

var link = vis.selectAll("line.link")
  .data(graph.links)
  .enter().append("svg:line")
  .attr("class", "link");
link.append("title").text(function(d) {
  return d.source.name + " -> " + d.target.name
});

var node = vis.selectAll("g.node")
  .data(graph.nodes)
  .enter().append("svg:g")
  .attr("class", "node")
  .call(force.drag);
node.append("circle").attr("r", 5);
node.append("title").text(function(d) {
  return d.name
});


force.on("tick", function() {
  link.attr("x1", function(d) {
      return d.source.x;
    })
    .attr("y1", function(d) {
      return d.source.y;
    })
    .attr("x2", function(d) {
      return d.target.x;
    })
    .attr("y2", function(d) {
      return d.target.y;
    });

  node.attr("transform", function(d) {
    return "translate(" + d.x + "," + d.y + ")";
  });
});
line {
  stroke: red;
  stroke-width: 3;
}
cicrle {
  fill: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
  1. 或者我必须在鼠标悬停时显示 div?如何获取鼠标的位置? div 将显示在哪里?

回答:

如果您想显示带有 html 内容的工具提示,您可以在鼠标悬停时显示 div。您可以使用d3.mouse 方法获取鼠标位置。

演示:

var width = 960;
var height = 500;
var margin = 20;
var pad = margin / 2;

var color = d3.scale.category20();


var graph = {
  "nodes": [{
    "name": "Myriel",
    "group": 1
  }, {
    "name": "Napoleon",
    "group": 1
  }, {
    "name": "Mlle.Baptistine",
    "group": 1
  }, {
    "name": "Mme.Magloire",
    "group": 1
  }, {
    "name": "CountessdeLo",
    "group": 1
  }, {
    "name": "Geborand",
    "group": 1
  }, {
    "name": "Champtercier",
    "group": 1
  }, {
    "name": "Cravatte",
    "group": 1
  }, {
    "name": "Count",
    "group": 1
  }, {
    "name": "OldMan",
    "group": 1
  }, {
    "name": "Labarre",
    "group": 2
  }, {
    "name": "Valjean",
    "group": 2
  }, {
    "name": "Marguerite",
    "group": 3
  }, {
    "name": "Mme.deR",
    "group": 2
  }, {
    "name": "Isabeau",
    "group": 2
  }, {
    "name": "Gervais",
    "group": 2
  }, {
    "name": "Tholomyes",
    "group": 3
  }, {
    "name": "Listolier",
    "group": 3
  }, {
    "name": "Fameuil",
    "group": 3
  }, {
    "name": "Blacheville",
    "group": 3
  }, {
    "name": "Favourite",
    "group": 3
  }, {
    "name": "Dahlia",
    "group": 3
  }, {
    "name": "Zephine",
    "group": 3
  }, {
    "name": "Fantine",
    "group": 3
  }, {
    "name": "Mme.Thenardier",
    "group": 4
  }, {
    "name": "Thenardier",
    "group": 4
  }, ],
  "links": [{
    "source": 1,
    "target": 0,
    "value": 1
  }, {
    "source": 2,
    "target": 0,
    "value": 8
  }, {
    "source": 3,
    "target": 0,
    "value": 10
  }, {
    "source": 3,
    "target": 2,
    "value": 6
  }, {
    "source": 4,
    "target": 0,
    "value": 1
  }, {
    "source": 5,
    "target": 0,
    "value": 1
  }, {
    "source": 6,
    "target": 0,
    "value": 1
  }]
};
drawGraph(graph);

function drawGraph(graph) {
  var svg = d3.select("#force").append("svg")
    .attr("width", width)
    .attr("height", height);

  // draw plot background
  svg.append("rect")
    .attr("width", width)
    .attr("height", height)
    .style("fill", "#eeeeee");

  // create an area within svg for plotting graph
  var plot = svg.append("g")
    .attr("id", "plot")
    .attr("transform", "translate(" + pad + ", " + pad + ")");

  // https://github.com/mbostock/d3/wiki/Force-Layout#wiki-force
  var layout = d3.layout.force()
    .size([width - margin, height - margin])
    .charge(-120)
    .linkDistance(function(d, i) {
      return (d.source.group == d.target.group) ? 50 : 100;
    })
    .nodes(graph.nodes)
    .links(graph.links)
    .start();

  drawLinks(graph.links);
  drawNodes(graph.nodes);

  // add ability to drag and update layout
  // https://github.com/mbostock/d3/wiki/Force-Layout#wiki-drag
  d3.selectAll(".node").call(layout.drag);

  // https://github.com/mbostock/d3/wiki/Force-Layout#wiki-on
  layout.on("tick", function() {
    d3.selectAll(".link")
      .attr("x1", function(d) {
        return d.source.x;
      })
      .attr("y1", function(d) {
        return d.source.y;
      })
      .attr("x2", function(d) {
        return d.target.x;
      })
      .attr("y2", function(d) {
        return d.target.y;
      });

    d3.selectAll(".node")
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      });
  });
}

// Draws nodes on plot
function drawNodes(nodes) {
  // used to assign nodes color by group
  var color = d3.scale.category20();

  // https://github.com/mbostock/d3/wiki/Force-Layout#wiki-nodes
  d3.select("#plot").selectAll(".node")
    .data(nodes)
    .enter()
    .append("circle")
    .attr("class", "node")
    .attr("id", function(d, i) {
      return d.name;
    })
    .attr("cx", function(d, i) {
      return d.x;
    })
    .attr("cy", function(d, i) {
      return d.y;
    })
    .attr("r", function(d, i) {
      return 4;
    })
    .style("fill", function(d, i) {
      return color(d.group);
    })
    .on("mouseover", function(d, i) {

      var x = d3.mouse(this)[0];
      var y = d3.mouse(this)[1];
      var tooltip = d3.select("#plot")
        .append("text")
        .text(d.name)
        .attr("x", x)
        .attr("y", y)
        //.attr("dy", -r * 2)
        .attr("id", "tooltip");

    })
    .on("mouseout", function(d, i) {
      d3.select("#tooltip").remove();
    });
}

// Draws edges between nodes
function drawLinks(links) {
  var scale = d3.scale.linear()
    .domain(d3.extent(links, function(d, i) {
      return d.value;
    }))
    .range([1, 6]);

  // https://github.com/mbostock/d3/wiki/Force-Layout#wiki-links
  d3.select("#plot").selectAll(".link")
    .data(links)
    .enter()
    .append("line")
    .attr("class", "link")
    .attr("x1", function(d) {
      return d.source.x;
    })
    .attr("y1", function(d) {
      return d.source.y;
    })
    .attr("x2", function(d) {
      return d.target.x;
    })
    .attr("y2", function(d) {
      return d.target.y;
    })
    .style("stroke-width", function(d, i) {
      return scale(d.value) + "px";
    })
    .style("stroke-dasharray", function(d, i) {
      return (d.value <= 1) ? "2, 2" : "none";
    }).on("mouseover", function(d, i) {

      var x = d3.mouse(this)[0];
      var y = d3.mouse(this)[1];
      var tooltip = d3.select("#plot")
        .append("text")
        .text(d.source.name + " -> " + d.target.name)
        .attr("x", x)
        .attr("y", y)
        //.attr("dy", -r * 2)
        .attr("id", "tooltip");
    })
    .on("mouseout", function(d, i) {
      d3.select("#tooltip").remove();
    });

}
body {
  font-family: 'Source Sans Pro', sans-serif;
  font-weight: 300;
}
b {
  font-weight: 900;
}
.outline {
  fill: none;
  stroke: #888888;
  stroke-width: 1px;
}
#tooltip {
  font-size: 10pt;
  font-weight: 900;
  fill: #000000;
  stroke: #ffffff;
  stroke-width: 0.25px;
}
.node {
  stroke: #ffffff;
  stroke-weight: 1px;
}
.link {
  fill: none;
  stroke: #888888;
  stroke-weight: 1px;
  stroke-opacity: 0.5;
}
.highlight {
  stroke: red;
  stroke-weight: 4px;
  stroke-opacity: 1.0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div align="center" id="force"></div>

【讨论】:

    【解决方案2】:

    您可以将&lt;title&gt; 元素用于line 元素,就像链接的演示用于circle 元素一样。例如,

    link.append("title")
      .text(function(d) { return "This is my title"; });
    

    将在链接的演示中创建此类工具提示。请注意,according to the spec,您应该确保 title 元素是父元素的第一个子元素。

    另外请注意,这可能不是与用户交流的有效方式;悬停在一条线上是相当困难的,尤其是在移动图形中。一种可能的解决方案是将每一行包裹在一个更大的组中,并使用一条宽度更大的不可见线,并将标题添加为该组的第一个子项,因为标题元素也适用于 &lt;g&gt; 元素。

    【讨论】:

    • 能否提供代码sn-p或详细解释一下悬停细线的建议解决方案。
    猜你喜欢
    • 2021-10-02
    • 1970-01-01
    • 1970-01-01
    • 2014-06-08
    • 1970-01-01
    • 2016-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多