【问题标题】:D3 4.0 Graph with directed edges and labelsD3 4.0 有向边和标签的图
【发布时间】:2026-01-28 17:40:01
【问题描述】:

我正在尝试使用 d3 (v4.0) 创建一个图形,该图形具有节点的有向边和标签,如下所示:http://jsfiddle.net/chrisJamesC/HgHqy/ 但使用较新版本的 D3。

这是我目前所拥有的:https://jsfiddle.net/4nu57pgn/1/,但我似乎不知道如何告诉 D3 使边缘在视觉上定向(带有箭头)或显示节点 ID。

var svg = d3.select("svg");
var width = svg.attr("width");
var height = svg.attr("height");
svg = svg.call(d3.zoom().on("zoom", zoomed)).append("g");
var color = d3.scaleOrdinal(d3.schemeCategory10);

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));

function createGraph (error, graph) {
  if (error) throw error;

  var link = svg.append("g")
      .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("line")
      .attr("stroke", function(d) { return color(d.type); });


  var node = svg.append("g")
      .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .enter().append("circle")
      .attr("r", 10)
      .attr("fill", function(d) { if (d.root == "true") return color(d.root); return color(d.type); })
      .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended));

  node.on("click",function(d){
    console.log("clicked", d.id);
  });

  node.append("title")
      .text(function(d) { return d.id; });

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

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

  function ticked() {
    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("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
  }
}

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}

function zoomed() {
  svg.attr("transform", "translate(" + d3.event.transform.x + "," + d3.event.transform.y + ")" + " scale(" + d3.event.transform.k + ")");
}

我是 D3 的新手,如果有任何帮助指出我正确的方向,我将不胜感激。

【问题讨论】:

  • 看看this
  • @SaeedAdelMehraban 这个问题是针对 d3 v4.0 的。
  • @Owen 你总是可以将 v3 移植到 v4。
  • 只有我觉得他要求“指出正确的方向”很有趣吗?

标签: javascript d3.js graph


【解决方案1】:

箭头

要在行尾添加箭头,您需要使用 SVG 定义一个标记。只需稍作修改即可更新 d3 v3 代码以使用 v4 执行此操作。您提供的示例设置了三个箭头定义(每种关系类型一个),但要创建一个,您可以使用:

svg.append("defs").append("marker")
    .attr("id", "arrow")
    .attr("viewBox", "0 -5 10 10")
    .attr("refX", 20)
    .attr("refY", 0)
    .attr("markerWidth", 8)
    .attr("markerHeight", 8)
    .attr("orient", "auto")
  .append("svg:path")
    .attr("d", "M0,-5L10,0L0,5");

markerWidthmarkerHeightrefXrefY 的值很重要,并决定了标记的位置 - 不幸的是,我不知道它们如何与 viewBox 或圆半径相互作用,所以这些是通过反复试验选择的。

无需将标记捕获为变量,因为它将使用 URL 说明符来引用,如下所示:

  var link = svg.append("g")
      .attr("class", "links")
    .selectAll("line")
      .data(graph.links)
    .enter().append("line")
      .attr("stroke", function(d) { return color(d.type); })
      .attr("marker-end", "url(#arrow)");

标签

同样,对 v3 代码稍作修改即可。文本标签需要单独附加到 SVG,而不是作为节点的子节点,并在 ticked 函数中独立翻译。

设置标签:

var text = svg.append("g").attr("class", "labels").selectAll("g")
    .data(graph.nodes)
  .enter().append("g");

text.append("text")
    .attr("x", 14)
    .attr("y", ".31em")
    .style("font-family", "sans-serif")
    .style("font-size", "0.7em")
    .text(function(d) { return d.id; });

然后在力模拟滴答作响时将它们翻译到正确的位置:

  function ticked() {
    // ...
    text
        .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
  }

水平偏移由设置标签时x 的值定义(本例中为14)。

一起

完整示例请参见this fiddle

【讨论】:

  • 谢谢,这正是我需要的!
  • 我会在小提琴 CSS 中添加一个“.labels {pointer-events: none;}”,这样文本就无法点击了。