【问题标题】:How to update a tree graph in D3如何在 D3 中更新树形图
【发布时间】:2017-06-21 19:21:03
【问题描述】:

当我在 d3 中使用树形图时,我通过在树形图中添加一个节点来获得我想要的行为。但是,当我向图中添加节点时,节点(和路径)被放置,但节点没有平衡,之前的节点也没有移动。

我在这里发布了整个代码的工作版本:https://codepen.io/auser/pen/mwwVJL

JS代码(为了完整起见是):

const pathGraph = (eleName, treeData, opts = {}) => {
  var margin = { top: 40, right: 90, bottom: 50, left: 90 },
    width = 660 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

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

  const svg = d3
    .select(eleName)
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom);

  let g = svg
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  const updateData = newData => {
    //  assigns the data to a hierarchy using parent-child relationships
    let nodes = treemap(d3.hierarchy(newData));

    const color = d3.scaleOrdinal(d3.schemeCategory10).domain(d3.range(0, 8));

    // adds the links between the nodes
    const link = g
      .selectAll(".link")
      .data(nodes.descendants().slice(1))
      .enter()
      .append("path")
      .attr("class", "link")
      .style("stroke-width", 1)
      .attr("d", function(d) {
        return (
          "M" + d.x + "," + d.y + "C" + d.x + "," + (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + d.parent.y
        );
      });

    link.exit().remove();

    // adds each node as a group
    const node = g
      .selectAll(".node")
      .data(nodes.descendants())
      .enter()
      .append("g")
      .attr("class", function(d) {
        return "node" + (d.children ? " node--internal" : " node--leaf");
      })
      .attr("transform", function(d) {
        return "translate(" + d.x + "," + d.y + ")";
      });

    node.exit().remove();

  const parentTree = (d) => {
    let nodeLinks = []
    while(d.parent) {
      nodeLinks.push(d)
      d = d.parent
    }
    nodeLinks = nodeLinks.concat(d)
    return nodeLinks
  }

  const activeLink = (d, o) => {
    if (d === o || d.parent === o) return true;
  }
  node
    .on('mouseover', function(d) {
      const data = d3.select(this)
      const linkedNodes = parentTree(d)
      link
        .style('stroke-width', o => activeLink(d, o) ? 4 : 1)
        .style('stroke', o => activeLink(d, o) ? 'red' : '#333')
          .transition(500)

      rect
        .style('stroke-width', o => activeLink(d, o) ? 4 : 1)
        .transition(500)

    })
    .on('mouseout', d => {
      const data = d3.select(this);
      // console.log('d ->', d)
      link
        .style('stroke-width', 1)
        .style('stroke', '#333')

      rect
        .style('stroke-width', 1)
    })

    // adds the circle to the node
    const rect = node
      .append("rect")
      .attr("height", 50)
      .attr("width", 50)
      .style("fill", (d, i) => color(i))
      .attr("x", "-0.7em");

    // adds the text to the node
    node
      .append("text")
      .attr("dy", ".52em")
      .attr("y", function(d) {
        return d.children ? -18 : 20;
      })
      .attr("dx", "-.2em")
      .style("text-anchor", "middle")
      .text(function(d) {
        return d.data.name;
      });
  };

  updateData(treeData);
  return updateData;
};

const data =   {
    "name": "Root",
    "children": [
      {
		"name": "A",
        "children": [
          { "name": "B" },
          { "name": "C" }
        ]
      },
      { "name": "D" },
      { "name": "E",
        "children":[
          { "name": "F"}
        ] }
    ]
  };

const mount = document.querySelector('#treea')
const updateData = pathGraph(mount, data)

setTimeout(function() {
  data.children[2].children.push({ name: "H" })
  updateData(data, { update: true })
}, 2000)
.tree .node rect, .tree .node circle {
    fill: blue;
    rounding: 5px;
 }
 .tree .link {
    fill: none;
    stroke: #222;
    stroke-opacity: 1;
    stroke-width: 1.5px;
  }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>

<svg id="treea" class="tree"></svg>

任何帮助将不胜感激......

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    您需要正确的“进入”、“更新”和“退出”选择。像这样:

    //this is the update selection
    const link = g
        .selectAll(".link")
        .data(nodes.descendants().slice(1));
    
    //this is the enter selection, up to the 'merge'
    link.enter()
        .append("path")
        .attr("class", "link")
        .merge(link)//from now one, update + enter
        .style("stroke-width", 1)
        .attr("d", function(d) {
            return (
                "M" + d.x + "," + d.y + "C" + d.x + "," +
                (d.y + d.parent.y) / 2 + " " + d.parent.x + "," +
                (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + d.parent.y
            );
        });
    
    //this is the exit selection
    link.exit().remove();
    

    这里是更新的 Codepen:https://codepen.io/anon/pen/RggVPO?editors=0010

    这里是 Stack sn-p:

    const pathGraph = (eleName, treeData, opts = {}) => {
      var margin = { top: 40, right: 90, bottom: 50, left: 90 },
        width = 660 - margin.left - margin.right,
        height = 400 - margin.top - margin.bottom;
    
      const treemap = d3.tree().size([width, height]);
    
      const svg = d3
        .select(eleName)
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom);
    
      let g = svg
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
      const updateData = newData => {
        //  assigns the data to a hierarchy using parent-child relationships
        let nodes = treemap(d3.hierarchy(newData));
    
        const color = d3.scaleOrdinal(d3.schemeCategory20).domain(d3.range(0, 8));
    
        // adds the links between the nodes
        const link = g
          .selectAll(".link")
          .data(nodes.descendants().slice(1));
        
          link.enter()
          .append("path")
          .attr("class", "link")
          .merge(link)
          .style("stroke-width", 1)
          .attr("d", function(d) {
            return (
              "M" + d.x + "," + d.y + "C" + d.x + "," + (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + d.parent.y
            );
          }).lower();
    
        link.exit().remove();
        
        const node = g
          .selectAll(".node")
          .data(nodes.descendants())
        .attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
          });
        
        const nodeEnter = node.enter()
          .append("g")
          .attr("class", function(d) {
            return "node " + (d.children ? " node--internal" : " node--leaf");
          })
          .attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
          });
    
        node.exit().remove();
    
      const parentTree = (d) => {
        let nodeLinks = []
        while(d.parent) {
          nodeLinks.push(d)
          d = d.parent
        }
        nodeLinks = nodeLinks.concat(d)
        return nodeLinks
      }
    
      const activeLink = (d, o) => {
        if (d === o || d.parent === o) return true;
      }
      node
        .on('mouseover', function(d) {
          const data = d3.select(this)
          const linkedNodes = parentTree(d)
          link
            .style('stroke-width', o => activeLink(d, o) ? 4 : 1)
            .style('stroke', o => activeLink(d, o) ? 'red' : '#333')
              .transition(500)
    
          rect
            .style('stroke-width', o => activeLink(d, o) ? 4 : 1)
            .transition(500)
    
        })
        .on('mouseout', d => {
          const data = d3.select(this);
          // console.log('d ->', d)
          link
            .style('stroke-width', 1)
            .style('stroke', '#333')
    
          rect
            .style('stroke-width', 1)
        })
    
        // adds the circle to the node
        const rect = nodeEnter
          .append("rect")
          .attr("height", 50)
          .attr("width", 50)
          .style("fill", (d, i) => color(i))
          .style('padding', 5)
          .attr("rx", 6)
          .attr("ry", 6)
          .attr("x", "-0.7em");
    
        // adds the text to the node
        nodeEnter
          .append("text")
          .attr("dy", "0.8em")
          .attr("y", function(d) {
            return d.children ? -18 : 20;
          })
          .attr("dx", "0.8em")
          .style("text-anchor", "middle")
          .text(function(d) {
            return d.data.name;
          });
        
        node.select("rect").attr("x", "-0.7em");
        
      };
    
      updateData(treeData);
      return updateData;
    };
    
    const data =   {
        "name": "Root",
        "children": [
          {
    		"name": "A",
            "children": [
              { "name": "B" },
              { "name": "C" }
            ]
          },
          { "name": "D" },
          { "name": "E",
            "children":[
              { "name": "F"}
            ] }
        ]
      };
    
    const mount = document.querySelector('#treea')
    const updateData = pathGraph(mount, data)
    
    setTimeout(function() {
      data.children[2].children.push({ name: "H" })
      updateData(data, { update: true })
    }, 2000)
    .tree .node rect, .tree .node circle {
        fill: blue;
        rounding: 5px;
     }
     .tree .link {
        fill: none;
        stroke: #222;
        stroke-opacity: 1;
        stroke-width: 1.5px;
      }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
    
    <svg id="treea" class="tree"></svg>

    PS:节点的情况更复杂,因为您有包含矩形和文本的组。我很快更改了选择,但我建议您相应地重构这部分代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-29
      • 2015-01-20
      相关资源
      最近更新 更多