如何将两个父节点连接到一个子节点,以及如何实用地为树中的每个节点制作工具提示?在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)

查看:25
本文介绍了如何将两个父节点连接到一个子节点,以及如何实用地为树中的每个节点制作工具提示?在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.

例如-如果您运行代码,您将更清楚地看到它.我想制作一个汉娜的子节点并标记,它将连接到名为"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天全站免登陆