代码之家  ›  专栏  ›  技术社区  ›  Acy

通过父节点和子节点d3js的位置追加圆

  •  0
  • Acy  · 技术社区  · 7 年前

    picture 我想在节点之间附加一个按钮。节点具有父关系和子关系。带加号的两个白色圆圈是coursera图片的子对象。因此,我试图通过调用一个函数来附加按钮,该函数取父对象(x,y)和子对象(x,y),然后在那里创建一个圆。 这是我的完整代码,我没有写这段代码的大部分,所以我没有完全理解。我认识到节点之间的链接路径是由以下函数绘制的:`函数对角线(s,d){

        var path = `M ${s.y} ${s.x}
            C ${(s.y + d.y) / 2} ${s.x},
              ${(s.y + d.y) / 2} ${d.x},
              ${d.y} ${d.x}`;
    
        return path;
      }`
    

    在我的代码中,如何在两个节点之间附加一个圆? 完整代码:`

    import React, { Component } from "react";
    import * as d3 from "d3";
    import { hierarchy, tree } from "d3-hierarchy";
    import "../Tree.css";
    
    //import "./Tree.css";
    class Tree extends Component {
      constructor(props) {
        super(props);
        this.state = { collapse: false, text: "hi", visible: true };
    
        //this.toggle = this.toggle.bind(this);
      }
    
      handleChange = d => {
        this.props.on_click_change(d);
      };
    
      componentDidMount() {
        var that = this;
        var treeData = this.props.roadmapData;
        // Set the dimensions and margins of the diagramS
        var height1 = window.innerHeight;
    
        var margin = { top: 0, right: 0, bottom: 0, left: 0 },
          width = 1080 - margin.left - margin.right,
          height = 500 - margin.top - margin.bottom;
    
        // append the svg object to the body of the page
        // appends a 'group' element to 'svg'
        // moves the 'group' element to the top left margin
    
        var svg = d3
          .select("li")
    
          .append("svg")
    
          .call(
            d3.zoom().on("zoom", function() {
              svg.attr("transform", d3.event.transform);
            })
          )
          .attr("width", 1800 - margin.right - margin.left)
          .attr("height", 900 - margin.top - margin.bottom)
          .append("g")
          .attr("transform", "translate(" + +"," + margin.top + ")");
    
        var i = 0,
          duration = 500,
          root;
    
        // declares a tree layout and assigns the size
        var treemap = d3.tree().size([window.innerHeight, window.innerWidth]);
    
        root = d3.hierarchy(treeData, function(d) {
          return d.children;
        });
        root.x0 = height / 2;
        root.y0 = 0;
        console.log(this.props.treeData);
        // Collapse after the second level
        root.children.forEach(collapse);
    
        update(root);
    
        // Collapse the node and all it's children
        function collapse(d) {
          if (d.children) {
            d._children = d.children;
            d._children.forEach(collapse);
            d.children = null;
          }
        }
    
        function update(source) {
          // Assigns the x and y position for the nodes
    
          var treeData = treemap(root);
    
          // Compute the new tree layout.
          var nodes = treeData.descendants(),
            links = treeData.descendants().slice(1),
            more_button = treeData.descendants();
    
          // Normalize for fixed-depth.
          nodes.forEach(function(d) {
            d.y = d.depth * 180;
          });
    
          // ****************** Nodes section ***************************
    
          // Update the nodes...
          var node = svg.selectAll("g.node").data(nodes, function(d) {
            return d.id || (d.id = ++i);
          });
    
          // Enter any new modes at the parent's previous position.
          var nodeEnter = node
            .enter()
            .append("g")
            .attr("class", "node")
    
            //if deleted, bubbles come from the very top, is weird
            .attr("transform", function(d) {
              return "translate(" + source.y0 + "," + source.x0 + ")";
            });
    
          // Add Circle for the nodes
          nodeEnter
            .append("circle")
            .attr("class", "node")
            .attr("r", 1e-6)
            .style("fill", function(d) {
              return d._children ? "lightsteelblue" : "#fff";
            });
    
          /*
    // Add labels for the nodes
          nodeEnter
            .append("text")
            .attr("dy", 0)
            .attr("x", function(d) {
              return d.children || d._children ? -13 : 13;
            })
            .attr("text-anchor", function(d) {
              return d.children || d._children ? "end" : "start";
            })
            .text(function(d) {
              return d.data.name;
            });
    */
          var diameter = 30;
          nodeEnter
            .append("image")
            .on("click", click)
            .attr("xlink:href", function(d) {
              return d.data.website_image;
            })
            .attr("height", diameter * 2)
            .attr("transform", "translate(-30," + -30 + ")");
    
          // UPDATE
          var nodeUpdate = nodeEnter.merge(node);
    
          // Transition to the proper position for the node
          nodeUpdate
            .transition()
            .duration(duration)
            .attr("transform", function(d) {
              return "translate(" + d.y + "," + d.x + ")";
            });
    
          // Update the node attributes and style
          nodeUpdate
            .select("circle.node")
            .attr("r", diameter)
    
            .style("fill", function(d) {
              return d._children ? "lightsteelblue" : "#fff";
            })
            .attr("cursor", "pointer");
    
          nodeUpdate
            .append("circle")
            .on("click", click2)
            .attr("additional", "extra_circle")
            .attr("r", 10)
            .attr("transform", "translate(0," + -40 + ")");
          // Remove any exiting nodes
          var nodeExit = node
            .exit()
            .transition()
            .duration(duration)
            .attr("transform", function(d) {
              return "translate(" + source.y + "," + source.x + ")";
            })
            .remove();
    
          // On exit reduce the node circles size to 0
          nodeExit.select("circle").attr("r", 1e-6);
    
          // On exit reduce the opacity of text labels
          nodeExit.select("text").style("fill-opacity", 1e-6);
    
          // ****************** links section ***************************
    
          // Update the links...
          var link = svg.selectAll("path.link").data(links, function(d) {
            return d.id;
          });
    
          // Enter any new links at the parent's previous position.
          var linkEnter = link
            .enter()
            .insert("path", "g")
    
            .attr("class", "link")
            .style("fill", "red")
    
            .attr("d", function(d) {
              var o = { x: source.x0, y: source.y0 };
              return diagonal(o, o);
            });
    
          // UPDATE
          var linkUpdate = linkEnter.merge(link);
    
          // Transition back to the parent element position
          linkUpdate
    
            .transition()
            .duration(duration)
    
            .attr("d", function(d) {
              console.log(d, d.parent);
              return diagonal(d, d.parent);
            });
    
          // Remove any exiting links
          var linkExit = link
            .exit()
            .transition()
            .duration(duration)
            .attr("d", function(d) {
              var o = { x: source.x, y: source.y };
              return diagonal(o, o);
            })
            .remove();
    
          // Store the old positions for transition.
          nodes.forEach(function(d) {
            d.x0 = d.x;
            d.y0 = d.y;
          });
    
          // Creates a curved (diagonal) path from parent to the child nodes
          function diagonal(s, d) {
    
            var path = `M ${s.y} ${s.x}
                C ${(s.y + d.y) / 2} ${s.x},
                  ${(s.y + d.y) / 2} ${d.x},
                  ${d.y} ${d.x}`;
    
            return path;
          }
    
          // 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 click2(d) {
            console.log(d.data.name);
            that.setState({ text: d.data.details });
            that.handleChange(d);
          }
        }
      }
    
      render() {
        return null;
      }
    }
    
    export default Tree;
    

    1 回复  |  直到 7 年前
        1
  •  0
  •   Acy    7 年前

    多亏了Coola,以下是解决方案:

    `nodeEnter
    
            .append("circle")
            .attr("class", "extra_info")
            .on("click", function(d) {})
    
            .attr("cy", function(d) {
              if (d.parent != null) {
                d.x_pos = d.x;
                d.parent_x_pos = d.parent.x;
              }
              if (d.parent_x_pos != null) {
                return (d.x_pos + d.parent_x_pos) / 2 - d.x_pos;
              }
            })
            .attr("cx", -90)
            .attr("r", 7);`
    

    所以基本上,当你做函数(d){}而不是仅仅调用一件事时,d变量就会被传递,它包含很多信息,包括当前元素的父元素和子元素(如果有),以及xy位置,等等。d指的是我们所处的当前元素。因此,我们可以轻松地使用d.parent.x和d.x来计算位置,下面是它现在的样子: enter image description here

    顺便说一句,d3和svg的x和y有时看起来是相反的。如您所见:我决定cy,SVG圆的属性是通过d.x确定其y位置。