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

时间:2019-02-08 17:54:23

标签: d3.js

这是我现在的treeMap:picture 我想在节点之间添加一个按钮。节点具有父子关系。带加号的2白色圆圈是Coursera图片的子元素。因此,我试图通过调用一个接受父级(x,y)和子级(x,y)的函数来附加按钮,然后在其中创建一个圆。 这是我的完整代码,我没有编写大部分代码,所以我没有完全理解。我知道节点之间的链接路径是通过此函数绘制的:`function对角线(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;
  }`

我想在我的代码的2个节点之间附加一个圆吗? 完整代码:`

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 个答案:

答案 0 :(得分:0)

感谢Coola,这是解决方案:

Levels

因此,基本上,每当您执行function(d){}而不是仅调用单个操作时,d变量都会被传递,并且它包含许多信息,包括当前元素的父元素和子元素(如果有)的信息, d表示我们当前所在的元素。因此,我们可以轻松地使用d.parent.x和d.x来计算位置,这是现在的样子:enter image description here

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