【问题标题】:D3 Tree Chart with left and right nodes on every nodesD3 树形图,每个节点上都有左右节点
【发布时间】:2020-10-22 14:30:55
【问题描述】:

我有一个具有左右路径/节点的 d3 图表。现在我要做的是在单击节点时我想附加相同的数据(具有左右节点的同一棵树),并且这棵新树将根据单击的节点 x 和 y 值填充/居中,所以我尝试使用从单击的对象中获得的 x 和 y 值添加一个新的 g。

喜欢这个

       var g = svg.append("g")
            .attr("transform", "translate(" + d.x * 2 + "," + d.y + ")");
        drawTree2(left, "left", d);
        drawTree2(right, "right", d);

但它不起作用,请帮助

var data = {
  "name": "Root",
  "children": [{
      "name": "Branch 1"
    }, {
      "name": "Branch 2",
    }, {
      "name": "Branch 3"
    }, {
      "name": "Branch 4",
    }, {
      "name": "Branch 5"
    },


    {
      "name": "Branch 6"
    }, {
      "name": "Branch 7",
    }, {
      "name": "Branch 8"
    }, {
      "name": "Branch 9",
    }, {
      "name": "Branch 10"
    }
  ]
};


var split_index = Math.round(data.children.length / 2)

// Left data
var data1 = {
  "name": data.name,
  "children": JSON.parse(JSON.stringify(data.children.slice(0, split_index)))
};

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

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

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

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

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



function drawTree(root, pos) {

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

  // Create new default tree layout
  var tree = d3.tree()
    // Set the size
    // Remember the tree is rotated
    // so the height is used as the width
    // and the width as the height
    .size([height, SWITCH_CONST * (width - 150) / 2]);

  tree(root)

  var nodes = root.descendants();
  var links = root.links();
  // Set both root nodes to be dead center vertically
  nodes[0].x = height / 2

  // Create links
  var link = g.selectAll(".link." + pos)
    .data(links)
    .join(
      enter => enter.append("path"),
      update => update,
      exit => exit.remove()
    )
    .attr("class", "link " + pos)
    .attr("d", function(d) {
      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;
    });

  // Create nodes
  var node = g.selectAll(".node." + pos)
    .data(nodes)
    .join(
      enter => {
        const n = enter
          .append("g")
          .on("click", (e, d) => {
            drawSecondTree(d);
          });

        n.append("circle").attr("r", 2.5);
        n.append("text").attr("y", -10).style("text-anchor", "middle");
        return n;
      },
      update => update,
      exit => exit.remove()
    )
    .attr("class", function(d) {
      return "node " + pos + (d.children ? " node--internal" : " node--leaf");
    })
    .attr("transform", function(d) {
      return "translate(" + d.y + "," + d.x + ")";
    })
    .select("text")
    .text(function(d) {
      return d.data.name
    });
}

function drawSecondTree(d) {
  var g = svg.append("g")
    .attr("transform", "translate(" + d.x * 2 + "," + d.y + ")");
  drawTree2(left, "left", d);
  drawTree2(right, "right", d);

  function drawTree2(root, pos, d) {

    console.log(d.x, d.y);
    //return false;
    var SWITCH_CONST = 1;
    if (pos === "left") {
      SWITCH_CONST = -1;
    }

    // Create new default tree layout
    var tree = d3.tree()
      // Set the size
      // Remember the tree is rotated
      // so the height is used as the width
      // and the width as the height
      .size([height, SWITCH_CONST * (width - 150) / 2]);

    tree(root)

    var nodes = root.descendants();
    var links = root.links();
    // Set both root nodes to be dead center vertically
    nodes[0].x = d.y;

    // Create links
    var link = g.selectAll(".link." + pos)
      .data(links)
      .join(
        enter => enter.append("path"),
        update => update,
        exit => exit.remove()
      )
      .attr("class", "link " + pos)
      .attr("d", function(d) {
        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;
      });

    // Create nodes
    var node = g.selectAll(".node." + pos)
      .data(nodes)
      .join(
        enter => {
          const n = enter
            .append("g")
            .on("click", (e, d) => toggle(d, pos, pos === "left" ? left : right));

          n.append("circle").attr("r", 2.5);
          n.append("text").attr("y", -10).style("text-anchor", "middle");
          return n;
        },
        update => update,
        exit => exit.remove()
      )
      .attr("class", function(d) {
        return "node " + pos + (d.children ? " node--internal" : " node--leaf");
      })
      .attr("transform", function(d) {
        return "translate(" + d.y + "," + d.x + ")";
      })
      .select("text")
      .text(function(d) {
        return 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/6.2.0/d3.min.js"></script>
<svg width="900" height="600"></svg>

【问题讨论】:

  • 我没明白你的意思,你能给出你想要的行为的清晰步骤计划吗?你的意思是第二棵树附加到第一棵树上吗?还是画在它的地方?
  • 是的,第二棵树应该从被点击的节点开始,基本上第二棵树是被点击节点的子节点,但我的要求是子节点应该像父节点一样有左右节点,这就是为什么我'正在尝试追加一个新的 g。有没有更好的方法呢? @RubenHelsloot
  • 如果节点在根的左边,右边的子节点不会和根的链接重叠吗?
  • 也许this 更接近你想要的
  • 重叠很好@RubenHelsloot

标签: javascript d3.js charts tree


【解决方案1】:

我认为我明白你的意思。

很难解释,因为设置方式有点混乱。基本上,主要问题是您创建新g 元素的“中心点”(translate(x,y) 部分)的方式。令人困惑的原因是您在某些地方切换 X 和 Y 坐标。也许你无法通过你想要的地图外观来解决这个问题,这很好,只是很难遵循。

我在drawSecondTree 函数中做了以下更改(注意“添加”和“更新”项):

function drawSecondTree(d) {
  var gX = (width / 2) + d.y; // ********** added
  var gY = d.x - gX; // ********** added
  
  var g = svg.append("g")
    .attr("transform", "translate(" + gX + "," + gY + ")"); // ********** updated

  drawTree2(left, "left", d);
  drawTree2(right, "right", d);

  function drawTree2(root, pos, d) {

    console.log(d.x, d.y);
    //return false;
    var SWITCH_CONST = 1;
    if (pos === "left") {
      SWITCH_CONST = -1;
    }

    // Create new default tree layout
    var tree = d3.tree()
      // Set the size
      // Remember the tree is rotated
      // so the height is used as the width
      // and the width as the height
      .size([height, SWITCH_CONST * (width - 150) / 2]);

    tree(root)

    var nodes = root.descendants();
    var links = root.links();
    // Set both root nodes to be dead center vertically
    // nodes[0].x = d.y;
    nodes[0].x = (width / 2) + d.y; // ********** updated

    // Create links
    var link = g.selectAll(".link." + pos)
      .data(links)
      .join(
        enter => enter.append("path"),
        update => update,
        exit => exit.remove()
      )
      .attr("class", "link " + pos)
      .attr("d", function(d) {
        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;
      });

    // Create nodes
    var node = g.selectAll(".node." + pos)
      .data(nodes)
      .join(
        enter => {
          const n = enter
            .append("g")
            .on("click", (e, d) => toggle(d, pos, pos === "left" ? left : right));

          n.append("circle").attr("r", 2.5);
          n.append("text").attr("y", -10).style("text-anchor", "middle");
          return n;
        },
        update => update,
        exit => exit.remove()
      )
      .attr("class", function(d) {
        return "node " + pos + (d.children ? " node--internal" : " node--leaf");
      })
      .attr("transform", function(d) {
        return "translate(" + d.y + "," + d.x + ")";
      })
      .select("text")
      .text(function(d) {
        return d.data.name
      });
  }
}

这是完整代码的codepen(节点重叠)。

我还建议研究使用svg 元素的viewBox,并通过单击它们来了解如何在添加更多节点时进行缩小。

【讨论】:

  • 是的,这就是我想要做的。单击右节点时,即使两个树的宽度和高度相同,新树节点也没有正确对齐。是否试图避免重叠?有没有更好的方法呢?我搜索了很多,但找不到任何解决方案@Joseph
  • "有更好的方法吗?" - 我昨晚在想这个问题。我认为解决此问题的最佳方法如下:单击节点时,修改主数据节点并让update 函数处理重新计算树。请参阅this doc 了解一些见解。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-21
  • 2023-03-15
相关资源
最近更新 更多