如何将两个父节点连接到一个子节点以及如何务实地为树中的每个节点制作工具提示?在 D3 js (SVG) [英] How can I connect two parent node into one child node and how to make a tooltip for each node in the tree pragmatically? in D3 js (SVG)

查看:13
本文介绍了如何将两个父节点连接到一个子节点以及如何务实地为树中的每个节点制作工具提示?在 D3 js (SVG)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我制作了一个树形图结构,它工作得非常好.但现在我想做一个小小的改变却无法做出改变.我想将两个父节点连接到一个子节点......我还试图务实地向每个节点添加一个工具提示.

I have made a tree graph structure and it's working absolutely fine. But now I want to make a small change but unable to make that change. I want to connect the two-parent node into one child node...also I am trying to add a tooltip into each node pragmatically.

例如——如果你运行代码,你会更清楚地看到它.我想制作 Hanna 的子节点并标记它将连接到名为Eric"的子节点.

For example - if you run the code, you will see it more clearly. I wanna make a child node of Hanna and mark which will be connected to a child node named "Eric".

知道如何实现这一目标吗?

any idea how to achieve this?

var svg = d3
  .select("body")
  .append("svg")
  .attr("width", 600)
  .attr("height", 600)
  .append("g")
  .attr("transform", "translate(50,50)");

//tree data
var data = [
  { child: "John", parent: "" },
  { child: "Aron", parent: "Kevin" },
  { child: "Kevin", parent: "John" },
  { child: "Hannah", parent: "Anna" },
  { child: "Rose", parent: "Sarah" },
  { child: "Anna", parent: "John" },
  { child: "Sarah", parent: "Kevin" },
  { child: "Mark", parent: "Anna" },
  { child: "Angle", parent: "Sarah" },
];

//to construct
var dataStructure = d3
  .stratify()
  .id(function (d) {
    return d.child;
  })
  .parentId(function (d) {
    return d.parent;
  })(data);

//to define the size of the structure tree
var treeStructure = d3.tree().size([500, 300]);
var information = treeStructure(dataStructure);

//to make the connections curves
var connections = svg.append("g").selectAll("path").data(information.links());
connections
  .enter()
  .append("path")
  .attr("d", function (d) {
    return (
      "M" +
      d.source.x +
      "," +
      d.source.y +
      " C " +
      d.source.x +
      "," +
      (d.source.y + d.target.y) / 2 +
      " " +
      d.target.x +
      "," +
      (d.source.y + d.target.y) / 2 +
      " " +
      d.target.x +
      "," +
      d.target.y
    );
  });

//creating the circles with data info
var circles = svg
  .append("g")
  .selectAll("circle")
  .data(information.descendants());

//placing the circles
circles
  .enter()
  .append("circle")
  .attr("cx", function (d) {
    return d.x;
  })
  .attr("cy", function (d) {
    return d.y;
  })
  .attr("r", 7)
  .append("text");

//names
var names = svg.append("g").selectAll("text").data(information.descendants());
names
  .enter()
  .append("text")
  .text(function (d) {
    return d.data.child;
  })
  .attr("x", function (d) {
    return d.x + 7;
  })
  .attr("y", function (d) {
    return d.y + 4;
  });

circle {
  fill: rgb(88, 147, 0);
}

path {
  fill: none;
  stroke: black;
}

<script src="https://d3js.org/d3.v6.min.js"></script>

推荐答案

你能做的最简单的事情就是随机分配一个父级,然后使用该父级来定位节点,并自己绘制链接.

The simplest thing you can do is just assign a parent at random, then use that parent to position the nodes, and draw the links yourself.

var svg = d3
  .select("body")
  .append("svg")
  .attr("width", 600)
  .attr("height", 600)
  .append("g")
  .attr("transform", "translate(50,50)");

//tree data
var data = [
  { child: "Alice", parents: [] },
  { child: "Bob", parents: [] },
  { child: "Carol", parents: [] },
  { child: "Dave", parents: ["Alice", "Bob"] },
  { child: "Eve", parents: ["Alice", "Bob"] },
  { child: "Francis", parents: ["Bob", "Carol"] },
  { child: "Graham", parents: ["Carol"] },
  { child: "Hugh", parents: ["Eve", "Graham"] },
];

// Process the nodes, add a pseudo root node so we don't have
// multiple roots
data.forEach(function(d) {
  d.parentId = d.parents.length > 0 ? d.parents[0] : "root";
});
data.unshift({ child: "root", parentId: "" });

//to construct
var dataStructure = d3
  .stratify()
  .id(function (d) {
    return d.child;
  })
  .parentId(function (d) {
    return d.parentId;
  })(data);

//to define the size of the structure tree
var treeStructure = d3.tree().size([500, 300]);
var root = treeStructure(dataStructure);

var nodes = root.descendants()
  .filter(function(d) { return d.id !== "root"; });

// Custom way to get all links we need to draw
var links = [];
nodes.forEach(function(node) {
  node.data.parents.forEach(function(parentId) {
    var parentNode = nodes.find(function(d) { return d.id === parentId; });
    links.push({
      source: parentNode,
      target: node,
    });
  });
});

//to make the connections curves
var connections = svg.append("g").selectAll("path").data(links);
connections
  .enter()
  .append("path")
  .attr("d", function (d) {
    return (
      "M" +
      d.source.x +
      "," +
      d.source.y +
      " C " +
      d.source.x +
      "," +
      (d.source.y + d.target.y) / 2 +
      " " +
      d.target.x +
      "," +
      (d.source.y + d.target.y) / 2 +
      " " +
      d.target.x +
      "," +
      d.target.y
    );
  });

//creating the circles with data info
var circles = svg
  .append("g")
  .selectAll("circle")
  .data(nodes);

//placing the circles
circles
  .enter()
  .append("circle")
  .attr("cx", function (d) {
    return d.x;
  })
  .attr("cy", function (d) {
    return d.y;
  })
  .attr("r", 7)
  .append("text");

//names
var names = svg.append("g").selectAll("text").data(nodes);
names
  .enter()
  .append("text")
  .text(function (d) {
    return d.id;
  })
  .attr("x", function (d) {
    return d.x + 7;
  })
  .attr("y", function (d) {
    return d.y + 4;
  });

circle {
  fill: rgb(88, 147, 0);
}

path {
  fill: none;
  stroke: black;
}

<script src="https://d3js.org/d3.v6.min.js"></script>

但是,您可以看到这存在一些问题.我们希望链接尽可能短,所以我们应该按他们的父母对节点进行排序 - 具有相同父母的孩子应该在父母双方之间的某个地方,如果两个父母之间不应该有任何节点,如果他们有伙伴关系.

However, you can see that this has some problems. We'd want the links to be as short as possible, so we should order the nodes by their parents - children with the same parent should be somewhere between both parents, and there shouldn't be any nodes between two parents if they have a partnership.

可能最简单的方法是使用节点链接图.通过施加正确的力,您可以将节点推向最佳布局.

Possibly the simplest way to do that is by using a node-link diagram. By applying the right forces, you can push the nodes towards an optimal layout.

我强制链接很短,树居中在图表中间,节点在正确的级别,每个节点与任何其他节点相距 100 像素.

I force the links to be short, the tree to be centred in the middle of the diagram, the nodes to be at the correct levels, and each node to be 100 pixels away from any other nodes.

我运行了 100 次模拟并在渲染任何内容之前停止它,从而避免了您通常看到的移动和抽搐.就用户体验而言,它看起来像一个普通的树状结构.

I run 100 ticks of the simulation and stop it before ever rendering anything, thus avoiding the movement and twitching that you normally see. As far as user experience goes, it looks like a normal tree-like structure.

我还在节点上使用自定义属性 d.dy 作为所需的 y 值".该值是模拟时吸引节点的值,但由于它可能会稍有偏差,因此可能看起来很混乱.通过像这样捕捉 y 轴值,我避免了这种情况并使其看起来更有条理.

I also use a custom attribute d.dy on the nodes as the "desired y-value". This value is the one that the nodes are attracted to for the simulation, but since it can deviate a little bit, it can look messy. By snapping the y-axis values like this, I avoided that and made it look more structured.

var levelRadius = 50;
var radius = 10;
var size = 500;
var margin = 50;

var svg = d3
  .select("body")
  .append("svg")
  .attr("width", size + 2 * margin)
  .attr("height", size + 2 * margin)
  .append("g")
  .attr("transform", "translate(" + [margin, margin] + ")");

//tree data
var data = [{
    child: "Alice",
    parents: []
  },
  {
    child: "Bob",
    parents: []
  },
  {
    child: "Carol",
    parents: []
  },
  {
    child: "Dave",
    parents: ["Alice", "Bob"]
  },
  {
    child: "Eve",
    parents: ["Alice", "Bob"]
  },
  {
    child: "Francis",
    parents: ["Bob", "Carol"]
  },
  {
    child: "Graham",
    parents: ["Carol"]
  },
  {
    child: "Hugh",
    parents: ["Eve", "Graham"]
  },
];

data.forEach(function(d, i) {
  d.level = d.parents.length ?
    data.find(function(p) {
      return p.child === d.parents[0];
    }).level + 1 :
    0;

  // Desired y level, otherwise try for sensible defaults
  d.dy = d.y = d.level * levelRadius * 2;
  d.x = size / 2 - i * levelRadius;
});

var nodes = data;

// Custom way to get all links we need to draw
var links = [];
nodes.forEach(function(node) {
  node.parents.forEach(function(parentId) {
    var parentNode = nodes.find(function(d) {
      return d.child === parentId;
    });
    links.push({
      id: parentNode.child + " - " + node.child,
      source: parentNode,
      target: node,
    });
  });
});

var simulation = d3.forceSimulation(nodes)
  .force("link", d3.forceLink(links).id(d => d.id))
  .force("collide", d3.forceCollide(levelRadius))
  .force("x", d3.forceX()
    .x(function(d) {
      return size / 2;
    })
    .strength(0.2)
  )
  .force("y", d3.forceY()
    .y(function(d) {
      return d.dy;
    })
    .strength(5)
  );
// Run the simulation once, even before rendering anything
simulation.tick(100)
  .stop();

//to make the connections curves
var link = svg.append("g").selectAll("path")
  .data(links)
  .enter()
  .append("path")
  .attr("d", function(d) {
    return (
      "M" +
      d.source.x +
      "," +
      d.source.dy +
      " C " +
      d.source.x +
      "," +
      (d.source.y + d.target.dy) / 2 +
      " " +
      d.target.x +
      "," +
      (d.source.y + d.target.dy) / 2 +
      " " +
      d.target.x +
      "," +
      d.target.dy
    );
  });

var node = svg
  .selectAll(".node")
  .data(nodes)
  .enter()
  .append("g")
  .attr("class", "node")
  .attr("transform", function(d) {
    // Use d.dy to snap the node to the level that we want it at
    return "translate(" + [d.x, d.dy] + ")";
  });

node.append("circle")
  .attr("r", radius);
node.append("text")
  .attr("dominant-baseline", "middle")
  .attr("dx", radius + 3)
  .text(function(d) {
    return d.child;
  });

circle {
  fill: rgb(88, 147, 0);
}

path {
  fill: none;
  stroke: black;
}

<script src="https://d3js.org/d3.v6.min.js"></script>

这篇关于如何将两个父节点连接到一个子节点以及如何务实地为树中的每个节点制作工具提示?在 D3 js (SVG)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆