在有很多子节点时如何显示所有子节点

时间:2020-05-20 18:14:39

标签: javascript css d3.js

我正在D3可视化上工作,该可视化显示了父子关系。

当我的孩子数较少时,我可以可视化完美的数据,但是当孩子数较多时,子节点会重叠。如何修改图表,以便可以看到根(左侧)左侧的所有节点?例如,使节点围绕根以圆形方式存在,一些节点靠近根,而某些节点以某种顺序排列。蜜蜂展示它的最佳方式是什么?

这是我的代码。

var data = {
  "name": "Root",
  "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png",
  "children": [{
    "name": "3",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "4",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "1",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "2",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "1",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "2",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "1",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "2",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "1",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "2",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "1",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "2",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "1",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "2",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "1",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }, {
    "name": "2",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }],
  "parent": [{
    "name": "1",
    "img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
  }]
};
var bgColors = ['#fd90b5', '#6ca1e9', '#fa975c', '#eb7092', '#f88962', '#a094ed', '#7f8de1'];
var dr = 0;
// Left data
var data1 = {
  "name": data.name,
  "img": data.img,
  "children": JSON.parse(JSON.stringify(data.children))
};

// Right data
var data2 = {
  "name": data.name,
  "img": data.img,
  "children": JSON.parse(JSON.stringify(data.parent))
};

// Create d3 hierarchies
var right = d3.hierarchy(data1);
var left = d3.hierarchy(data2);

// Render both trees
drawTree(right, "right")
drawTree(left, "left")

// draw single tree
function drawTree(root, pos) {
  var refType;
  if (pos == 'left')
    refType = 'left';
  else
    refType = 'right';

  var SWITCH_CONST = 1;
  if (pos === "left") {
    SWITCH_CONST = -1;
  }

  var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height")

  var g = svg.append("g").attr("transform", "translate(" + width / 2 + ",0)");

  var tree = d3.tree()
    .size([height, SWITCH_CONST * (width - 150) / 2]);

  tree(root)

  var nodes = root.descendants();
  var links = root.links();
  nodes[0].x = height / 2

  // Create links
  var link = g.selectAll(".link")
    .data(links)
    .enter()

  link.append("path")
    .attr("class", "link")
    .attr("d", function(d) {
      //first return returns a curve and the second will return straight lines in
      //return "M" + d.target.y + "," + d.target.x + "C" + (d.target.y + d.source.y) / 2.5 + "," + d.target.x + " " + (d.target.y + d.source.y) / 2 + "," + d.source.x + " " + d.source.y + "," + d.source.x;
      return "M" + d.target.y + "," + d.target.x + "A" + dr + "," + dr + " 1,0 0 " + d.source.y + "," + d.source.x;

    });


  link.append("text")
    .attr("font-family", "Arial, Helvetica, sans-serif")
    .attr("fill", "Black")
    .style("font", "normal 12px Arial")
    .attr("transform", function(d) {
      return "translate(" +
        ((d.source.y + d.target.y) / 2) + "," +
        ((d.source.x + d.target.x) / 2) + ")";
    })
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .data(nodes)
    .text(refType);

  // Create nodes
  var node = g.selectAll(".node")
    .data(nodes)
    .enter()
    .append("g")
    .attr("class", function(d) {
      return "node" + (d.children ? " node--internal" : " node--leaf");
    })
    .attr("transform", function(d) {
      return "translate(" + d.y + "," + d.x + ")";
    })

  node.append('circle')
    .attr('class', 'icon-wrap')
    .attr('x', 0)
    .attr('y', 0)
    .attr('r', 25)
    .style('fill', 'black');


  node.append('image')
    .attr('href', d => d.data.img)
    .attr('x', '-25')
    .attr('y', '-25')
    .attr('height', '50')
    .attr('width', '50');

  node.append("text")
    .attr("dy", 45)
    .style("text-anchor", "middle")
    .text(d => d.data.name);
}
.node circle {
  fill: #999;
}

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

.node--internal circle {
  fill: #555;
}

.link {
  fill: none;
  stroke: #555;
  stroke-opacity: 0.4;
  stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg width="700" height="400"></svg>

这是孩子人数少的样子。

enter image description here

请让我知道如何优化代码以适应最多30个子节点。

谢谢

1 个答案:

答案 0 :(得分:1)

一个可能的解决方案是创建一个递归函数,该函数将调整public async Task PortHitmethod(Iterations iterations)函数中的root数据坐标。

这是一个递归函数,它将使节点左右错开。请注意注释,其中提到了代码的哪一部分控制坐标的计算。

drawTree

完整摘要:

function adjustClashes(data, siblings = 1, index = 1, radius = 20, height = 400) {
  //can the node fit in the current x level?
  // if not adjust it
  let heightneeded = siblings * radius * 2;
  if (heightneeded > height) {
    // the code in this if statement will control the calculations for your new coordinates
    // In the simplest case we adjust the nodes by staggering odd and even nodes
    if (index % 2 != 0){
      data.y = data.y + (radius * 2)
    } else {
      data.y = data.y - (radius * 2)
    }
  }

  // if there are children go deeper and perform same adjustment
  if (data.children) {
    data.children.forEach((f, i) => {
      return adjustClashes( f, data.children.length, i )
    })
  } else {
    return;
  }
  // finally return the data
  return data
}
var data = {
  name: "Root",
  img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png",
  children: [
    {
      name: "3",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "4",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "2",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "2",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "2",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "2",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "2",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "2",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "2",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    }
  ],
  parent: [
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    }
  ]
};

var bgColors = [
  "#fd90b5",
  "#6ca1e9",
  "#fa975c",
  "#eb7092",
  "#f88962",
  "#a094ed",
  "#7f8de1"
];
var dr = 0;
// Left data
var data1 = {
  name: data.name,
  img: data.img,
  children: JSON.parse(JSON.stringify(data.children))
};

// Right data
var data2 = {
  name: data.name,
  img: data.img,
  children: JSON.parse(JSON.stringify(data.parent))
};

// Create d3 hierarchies
var right = d3.hierarchy(data1);
var left = d3.hierarchy(data2);

// Render both trees
drawTree(right, "right");
drawTree(left, "left");

// draw single tree
function drawTree(root, pos) {
  var refType;
  if (pos == "left") refType = "left";
  else refType = "right";

  var SWITCH_CONST = 1;
  if (pos === "left") {
    SWITCH_CONST = -1;
  }

  var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

  var g = svg.append("g").attr("transform", "translate(" + width / 2 + ",0)");

  var tree = d3.tree().size([height, (SWITCH_CONST * (width - 150)) / 2]);

  tree(root);

  function adjustClashes(
    data,
    siblings = 1,
    index = 1,
    radius = 20,
    height = 400
  ) {
    //can the node fit in the current x level?
    // if not adjust it
    let heightneeded = siblings * radius * 2;
    if (heightneeded > height) {
      // the code in this if statement will control the calculations for your new coordinates
      // In the simplest case we adjust the nodes by staggering odd and even nodes
      if (index % 2 != 0) {
        data.y = data.y + radius * 2;
      } else {
        data.y = data.y - radius * 2;
      }
    }

    // if there are children go deeper and perform same adjustment
    if (data.children) {
      data.children.forEach((f, i) => {
        return adjustClashes(f, data.children.length, i);
      });
    } else {
      return;
    }
    // finally return the data
    return data;
  }

  root = adjustClashes(root);

  var nodes = root.descendants();
  var links = root.links();
  nodes[0].x = height / 2;

  // Create links
  var link = g.selectAll(".link").data(links).enter();

  link
    .append("path")
    .attr("class", "link")
    .attr("d", function (d) {
      //first return returns a curve and the second will return straight lines in
      //return "M" + d.target.y + "," + d.target.x + "C" + (d.target.y + d.source.y) / 2.5 + "," + d.target.x + " " + (d.target.y + d.source.y) / 2 + "," + d.source.x + " " + d.source.y + "," + d.source.x;
      return (
        "M" +
        d.target.y +
        "," +
        d.target.x +
        "A" +
        dr +
        "," +
        dr +
        " 1,0 0 " +
        d.source.y +
        "," +
        d.source.x
      );
    });

  link
    .append("text")
    .attr("font-family", "Arial, Helvetica, sans-serif")
    .attr("fill", "Black")
    .style("font", "normal 12px Arial")
    .attr("transform", function (d) {
      return (
        "translate(" +
        (d.source.y + d.target.y) / 2 +
        "," +
        (d.source.x + d.target.x) / 2 +
        ")"
      );
    })
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .data(nodes)
    .text(refType);

  // Create nodes
  var node = g
    .selectAll(".node")
    .data(nodes)
    .enter()
    .append("g")
    .attr("class", function (d) {
      return "node" + (d.children ? " node--internal" : " node--leaf");
    })
    .attr("transform", function (d) {
      return "translate(" + d.y + "," + d.x + ")";
    });

  node
    .append("circle")
    .attr("class", "icon-wrap")
    .attr("x", 0)
    .attr("y", 0)
    .attr("r", 25)
    .style("fill", "black");

  node
    .append("image")
    .attr("href", (d) => d.data.img)
    .attr("x", "-25")
    .attr("y", "-25")
    .attr("height", "50")
    .attr("width", "50");

  node
    .append("text")
    .attr("dy", 45)
    .style("text-anchor", "middle")
    .text((d) => d.data.name);
}
.node circle {
  fill: #999;
}

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

.node--internal circle {
  fill: #555;
}

.link {
  fill: none;
  stroke: #555;
  stroke-opacity: 0.4;
  stroke-width: 1.5px;
}

更新

下面的CodePen.io pen例如显示了三个层次的子节点交错,甚至增加了一点边距以更均匀地隔开它。

要执行此操作,只需将<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script> <svg width="700" height="400"></svg>作为默认参数添加到margin函数,并按如下所示更改if语句:

adjustClashes
相关问题