在d3js的力有向图的链接上显示工具提示

Show tool-tip on links of force directed graph in d3js

本文关键字:显示 链接 工具提示 有向图 d3js      更新时间:2023-09-26

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

  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>

  • 或者我必须在鼠标悬停时显示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>

    您可以对line元素使用<title>元素,就像链接的演示对circle元素所做的一样。例如,

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

    将在演示链接中创建这样的工具提示。注意,根据规范,您应该确保title元素是父元素的第一个子元素。

    还要注意,这可能不是与用户沟通的有效方式;悬停在一条线上是相当困难的,尤其是在移动的图形中。一种可能的解决方案是,在一个更大的组中使用更宽的不可见行来换行,并将title添加为该组的第一个子元素,因为title元素也可以很好地与<g>元素一起工作。