d3.js v4在可折叠树示例中的古怪链接转换 [英] d3.js v4 wacky link transition in collapsible tree example

查看:1046
本文介绍了d3.js v4在可折叠树示例中的古怪链接转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果你玩下面的折叠树,你会看到,当你到树的末端,并展开和折叠的节点,线在做一些古怪的东西,我不完全确定是什么驱动行为,如果我重写在此输入链接描述完全不适用。我去了一个平面数据结构,并使用分层将其转换为树布局。
到目前为止唯一的问题是行转换...任何想法?

If you play with the collapsible tree below you'll see that when you get to the end of the tree, and expand and collapse the nodes the lines are doing some wacky stuff and I am no entirely sure what drives the behavior or if my rewriting of enter link description here is completely off base. I went off a flat data structure and used stratify to transform it into a tree layout. The only issue so far is the line transitions...any thoughts?

var data = [{
    "name": "Hazer 5000",
    "parent": "CFO",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/stephen.jpg"
  }, {
    "name": "Employee 1",
    "parent": "Hazer 5000",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/cory.jpg"
  }, {
    "name": "Analytics Area",
    "parent": "Hazer 5000",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/matt.jpg"
  }, {
    "name": "Employee 2",
    "parent": "Hazer 5000",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/XinheZhang.jpg"
  }, {
    "name": "Employee 3",
    "parent": "Hazer 5000",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/craig.jpg"
  }, {
    "name": "Employee 4",
    "parent": "Hazer 5000",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/youri.jpg"
  }, {
    "name": "Intern 1",
    "parent": "Analytics Area",
    "img": ""
  }, {
    "name": "Inter 2",
    "parent": "Analytics Area",
    "img": ""
  }, {
    "name": "CFO",
    "parent": null,
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Brett.jpg"
  }, {
    "name": "CPA",
    "parent": "CFO",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Wes.jpg"
  }, {
    "name": "Matt's wife",
    "parent": "CPA",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Amy_R.jpg"
  }, {
    "name": "Employee 5",
    "parent": "CPA",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/DavidBriley.jpg"
  }, {
    "name": "Employee 6",
    "parent": "CPA",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/BrittanyAllred_.jpg"
  }, {
    "name": "Employee 7",
    "parent": "CPA",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Shea.jpg"
  }, {
    "name": "Employee 8",
    "parent": "Matt's wife",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Mindy.jpg"
  }, {
    "name": "Employee 9",
    "parent": "Matt's wife",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Jessica_Stacy.jpg"
  }, {
    "name": "Employee 10",
    "parent": "Matt's wife",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/FraleaneHudson.jpg"
  },{
    "name": "Employee 11",
    "parent": "Employee 9",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/MeganPierce_.jpg"
  },{
    "name": "Intern 3",
    "parent": "Employee 8",
    "img": ""
  }, {
    "name": "Intern 4",
    "parent": "Employee 8",
    "img": ""
  }

];

var margin = {top: 20, right: 120, bottom: 20, left: 120},
    width = 960 - margin.right - margin.left,
    height = 800 - margin.top - margin.bottom;

var i = 0,
    duration = 750,
    root;

var tree = d3.tree()
    .size([height, width]);

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.right + margin.left)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");


var stratify = d3.stratify()
  .id(function(d) {
    return d.name;//This position
  })
  .parentId(function(d) {
    return d.parent; //What position this position reports to
  });

var root = stratify(data);

root.each(function(d) {
  
    d.name = d.id; //transferring name to a name variable
    d.id = i; //Assigning numerical Ids
    i++;
  
  });

  root.x0 = height / 2;
  root.y0 = 0;

  function collapse(d) {
    if (d.children) {
      d._children = d.children;
      d._children.forEach(collapse);
      d.children = null;
    }
  }

  root.children.forEach(collapse);
  update(root);


d3.select(self.frameElement).style("height", "800px");

function update(source) {

  // Compute the new tree layout.
  var nodes = tree(root).descendants(),
      links = nodes.slice(1);

  // Normalize for fixed-depth.
  nodes.forEach(function(d) { d.y = d.depth * 180; });

  // Update the nodes…
  var node = svg.selectAll("g.node")
      .data(nodes, function(d) { return d.id || (d.id = ++i); });

  // Enter any new nodes at the parent's previous position.
  var nodeEnter = node.enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
      .on("click", click);

  nodeEnter.append("circle")
      .attr("r", 1e-6)
      .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

  nodeEnter.append("text")
      .attr("x", function(d) { return d.children || d._children ? -10 : 10; })
      .attr("dy", ".35em")
      .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
      .text(function(d) { return d.name; })
      .style("fill-opacity", 1e-6);

  // Transition nodes to their new position.
  var nodeUpdate = node.merge(nodeEnter).transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });

  nodeUpdate.select("circle")
      .attr("r", 4.5)
      .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

  nodeUpdate.select("text")
      .style("fill-opacity", 1);

  // Transition exiting nodes to the parent's new position.
  var nodeExit = node.exit().transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
      .remove();

  nodeExit.select("circle")
      .attr("r", 1e-6);

  nodeExit.select("text")
      .style("fill-opacity", 1e-6);


  // Update the links…
  var link = svg.selectAll("path.link")
      .data(links);
  
  // Transition links to their new position.
  link.transition()
      .duration(duration)
      .attr("d", connector);

  // Enter any new links at the parent's previous position.
  var linkEnter = link.enter().insert("path", "g")
                        .attr("class", "link")
                        .attr("d", function(d) {
        var o = {x: source.x0, y: source.y0, parent:{x: source.x0, y: source.y0}};
        return connector(o);
      });

  // Transition links to their new position.
  link.merge(linkEnter).transition()
      .duration(duration)
      .attr("d", connector);

  // Transition exiting nodes to the parent's new position.
  link.exit().transition()
      .duration(duration)
      .attr("d",  function(d) {
        var o = {x: source.x, y: source.y, parent:{x: source.x, y: source.y}};
        return connector(o);
      })
      .remove();

  // Stash the old positions for transition.
  nodes.forEach(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
}

// Toggle children on click.
function click(d) {
  if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
  update(d);
}

function connector(d) {
  return "M" + d.y + "," + d.x +
    "C" + (d.y + d.parent.y) / 2 + "," + d.x +
    " " + (d.y + d.parent.y) / 2 + "," + d.parent.x +
    " " + d.parent.y + "," + d.parent.x;
}

.node {
  cursor: pointer;
}

.node circle {
  fill: #fff;
  stroke: steelblue;
  stroke-width: 1.5px;
}

.node text {
  font: 10px sans-serif;
}

.link {
  fill: none;
  stroke: #ccc;
  stroke-width: 1.5px;

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

推荐答案

看起来像用于链接的数据绑定的键的问题。我指的是如果你调用 .data(links)没有第二个参数,d3使用 i 作为键,当它计算enter / update / exit集合,因此当你展开/折叠相同的链接可以有不同的 i 值。

Looks like an issue with the keys used for the link's data binding. I'm referring to the fact that if you call .data(links) without a 2nd argument, d3 uses i as the key when it computes the enter/update/exit sets, and so when you expand/collapse the same link can have different i values.

我相信我的工作,我所做的是添加第二个参数作为一个函数,通过结合节点的id和其父节点的id构造一个唯一的链接id。

I believe I got it working, and all I did was add that 2nd param as a function that constructs a unique link id by combining the node's id with its parent node's id.

而不是这

var link = svg.selectAll("path.link")
  .data(links)

我这样做了

var link = svg.selectAll("path.link")
  .data(links, function(link) { var id = link.id + '->' + link.parent.id; return id; });

尝试一下:

var data = [{
    "name": "Hazer 5000",
    "parent": "CFO",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/stephen.jpg"
  }, {
    "name": "Employee 1",
    "parent": "Hazer 5000",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/cory.jpg"
  }, {
    "name": "Analytics Area",
    "parent": "Hazer 5000",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/matt.jpg"
  }, {
    "name": "Employee 2",
    "parent": "Hazer 5000",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/XinheZhang.jpg"
  }, {
    "name": "Employee 3",
    "parent": "Hazer 5000",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/craig.jpg"
  }, {
    "name": "Employee 4",
    "parent": "Hazer 5000",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/youri.jpg"
  }, {
    "name": "Intern 1",
    "parent": "Analytics Area",
    "img": ""
  }, {
    "name": "Inter 2",
    "parent": "Analytics Area",
    "img": ""
  }, {
    "name": "CFO",
    "parent": null,
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Brett.jpg"
  }, {
    "name": "CPA",
    "parent": "CFO",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Wes.jpg"
  }, {
    "name": "Matt's wife",
    "parent": "CPA",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Amy_R.jpg"
  }, {
    "name": "Employee 5",
    "parent": "CPA",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/DavidBriley.jpg"
  }, {
    "name": "Employee 6",
    "parent": "CPA",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/BrittanyAllred_.jpg"
  }, {
    "name": "Employee 7",
    "parent": "CPA",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Shea.jpg"
  }, {
    "name": "Employee 8",
    "parent": "Matt's wife",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Mindy.jpg"
  }, {
    "name": "Employee 9",
    "parent": "Matt's wife",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Jessica_Stacy.jpg"
  }, {
    "name": "Employee 10",
    "parent": "Matt's wife",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/FraleaneHudson.jpg"
  },{
    "name": "Employee 11",
    "parent": "Employee 9",
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/MeganPierce_.jpg"
  },{
    "name": "Intern 3",
    "parent": "Employee 8",
    "img": ""
  }, {
    "name": "Intern 4",
    "parent": "Employee 8",
    "img": ""
  }

];

var margin = {top: 20, right: 120, bottom: 20, left: 120},
    width = 960 - margin.right - margin.left,
    height = 800 - margin.top - margin.bottom;

var i = 0,
    duration = 750,
    root;

var tree = d3.tree()
    .size([height, width]);

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.right + margin.left)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");


var stratify = d3.stratify()
  .id(function(d) {
    return d.name;//This position
  })
  .parentId(function(d) {
    return d.parent; //What position this position reports to
  });

var root = stratify(data);

root.each(function(d) {
  
    d.name = d.id; //transferring name to a name variable
    d.id = i; //Assigning numerical Ids
    i++;
  
  });

  root.x0 = height / 2;
  root.y0 = 0;

  function collapse(d) {
    if (d.children) {
      d._children = d.children;
      d._children.forEach(collapse);
      d.children = null;
    }
  }

  root.children.forEach(collapse);
  update(root);


d3.select(self.frameElement).style("height", "800px");

function update(source) {

  // Compute the new tree layout.
  var nodes = tree(root).descendants(),
      links = nodes.slice(1);

  // Normalize for fixed-depth.
  nodes.forEach(function(d) { d.y = d.depth * 180; });

  // Update the nodes…
  var node = svg.selectAll("g.node")
      .data(nodes, function(d) { return d.id || (d.id = ++i); });

  // Enter any new nodes at the parent's previous position.
  var nodeEnter = node.enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
      .on("click", click);

  nodeEnter.append("circle")
      .attr("r", 1e-6)
      .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

  nodeEnter.append("text")
      .attr("x", function(d) { return d.children || d._children ? -10 : 10; })
      .attr("dy", ".35em")
      .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
      .text(function(d) { return d.name; })
      .style("fill-opacity", 1e-6);

  // Transition nodes to their new position.
  var nodeUpdate = node.merge(nodeEnter).transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });

  nodeUpdate.select("circle")
      .attr("r", 4.5)
      .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

  nodeUpdate.select("text")
      .style("fill-opacity", 1);

  // Transition exiting nodes to the parent's new position.
  var nodeExit = node.exit().transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
      .remove();

  nodeExit.select("circle")
      .attr("r", 1e-6);

  nodeExit.select("text")
      .style("fill-opacity", 1e-6);


  // Update the links…
  var link = svg.selectAll("path.link")
      .data(links, function(link) { var id = link.id + '->' + link.parent.id; return id; });
  
  // Transition links to their new position.
  link.transition()
      .duration(duration)
      .attr("d", connector);

  // Enter any new links at the parent's previous position.
  var linkEnter = link.enter().insert("path", "g")
                        .attr("class", "link")
                        .attr("d", function(d) {
        var o = {x: source.x0, y: source.y0, parent:{x: source.x0, y: source.y0}};
        return connector(o);
      });

  // Transition links to their new position.
  link.merge(linkEnter).transition()
      .duration(duration)
      .attr("d", connector);

  // Transition exiting nodes to the parent's new position.
  link.exit().transition()
      .duration(duration)
      .attr("d",  function(d) {
        var o = {x: source.x, y: source.y, parent:{x: source.x, y: source.y}};
        return connector(o);
      })
      .remove();

  // Stash the old positions for transition.
  nodes.forEach(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
}

// Toggle children on click.
function click(d) {
  if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
  update(d);
}

function connector(d) {
  return "M" + d.y + "," + d.x +
    "C" + (d.y + d.parent.y) / 2 + "," + d.x +
    " " + (d.y + d.parent.y) / 2 + "," + d.parent.x +
    " " + d.parent.y + "," + d.parent.x;
}

.node {
  cursor: pointer;
}

.node circle {
  fill: #fff;
  stroke: steelblue;
  stroke-width: 1.5px;
}

.node text {
  font: 10px sans-serif;
}

.link {
  fill: none;
  stroke: #ccc;
  stroke-width: 1.5px;

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

这篇关于d3.js v4在可折叠树示例中的古怪链接转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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