Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 常规更新模式d3,删除旧数据_Javascript_Reactjs_D3.js_Svg - Fatal编程技术网

Javascript 常规更新模式d3,删除旧数据

Javascript 常规更新模式d3,删除旧数据,javascript,reactjs,d3.js,svg,Javascript,Reactjs,D3.js,Svg,我目前正在尝试使用d3(d3层次/d3力)在我的React应用程序上构建一个图表,我在理解常规模式更新的工作原理方面遇到了一个问题 我读了很多关于它的文章,但仍然对如何在代码中实现它感到困惑。更多信息,我目前的d3版本是5.15 下面是我当前用于绘制图表的代码: drawChart(data) { const root = d3.hierarchy(data); const links = root.links(); const nodes = root.descendants();

我目前正在尝试使用d3(d3层次/d3力)在我的React应用程序上构建一个图表,我在理解常规模式更新的工作原理方面遇到了一个问题

我读了很多关于它的文章,但仍然对如何在代码中实现它感到困惑。更多信息,我目前的d3版本是5.15

下面是我当前用于绘制图表的代码:

drawChart(data) {
  const root = d3.hierarchy(data);
  const links = root.links();
  const nodes = root.descendants();
  const width = this.props.width;
  const height = this.props.height;
  const leavesNumber = root.leaves().length;
  const initScale = 1 / Math.log(root.descendants().length);
  const initTransX = 1;
  const initTransY = initTransX * initScale;

  console.log("root =>", root);
  console.log("links =>", links);
  console.log("nodes =>", nodes);
  console.log("leavesNumber =>", leavesNumber);
  console.log("initScale =>", initScale)
  console.log("ancestors =>", root.depth);

  const simulation = d3.forceSimulation(nodes)
    .force("link", d3.forceLink(links).id(d => d.id).distance(
      d => {
      if (d.source.parent === null) {
        return 400
      } else {
        return 0
      }
    })
    .strength(1))
    .force("charge", d3.forceManyBody().strength(-50))
    .force("x", d3.forceX())
    .force("y", d3.forceY())
    //forceCollide create a radius around the node which will reject elements entering in
    .force("collide", d3.forceCollide(d => d.depth === 0 ? 400 : d.depth === 1 ? 200 : 100))
    .force('center', d3.forceCenter(0, 50))

  const zoom = d3.zoom()
    .scaleExtent([1, 5])
    .on("zoom", () => {
      const currentTransform = d3.event.transform;
      g.attr("transform", currentTransform.scale(initScale));
      slider.property("value", currentTransform.k);
    });

  const svg = d3.select("#container").append("svg")
    .attr("viewBox", [-width / 2, -height / 2, width, height])
    .attr("width", '100%')
    .attr("height", '100%')
    .attr("class", "carto-svg-container")
    .call(zoom)
    let g = svg.append("g").attr("id", "main-g-container").attr("transform","translate("+initTransX+","+initTransY+")scale("+initScale+","+initScale+")");

  function slided(d) {
    zoom.scaleTo(svg, d3.select(this).property("value"));
  }

  const slider = d3.select('#container').append("p").append("input")
    .datum({})
    .attr("type", "range")
    .attr("value", zoom.scaleExtent()[0])
    .attr("min", zoom.scaleExtent()[0])
    .attr("max", zoom.scaleExtent()[1])
    .attr("step", (zoom.scaleExtent()[1] - zoom.scaleExtent()[0]) / 100)
    .attr("class", "carto-slidebar")
    .on("input", slided)

    d3.select('#container').append("svg").attr("width", 24).attr("height", 24)
      .attr("class", "carto-zoom-out-button")
      .on("click", () => zoom.scaleBy(svg.transition().duration(750), 0.70))
      .append("g").attr("stroke", 'none').attr("stroke-width", "1").attr("fill", "none").attr("fill-rule", "evenodd")
      .append("g")
      .append("polygon").attr("fill", "#9AA5B8").attr("fille-rule", "nonzero").attr("points", "19 13 5 13 5 11 19 11")
      .append("polygon").attr("points", "0 0 24 0 24 24 0 24")

    d3.select('#container').append("svg").attr("width", 24).attr("height", 24)
      .attr("class", "carto-zoom-in-button")
      .on("click", () => zoom.scaleBy(svg.transition().duration(750), 1.5))
      .append("g").attr("stroke", 'none').attr("stroke-width", "1").attr("fill", "none").attr("fill-rule", "evenodd")
      .append("g")
      .append("polygon").attr("fill", "#647592").attr("fille-rule", "nonzero").attr("points", "19 13 13 13 13 19 11 19 11 13 5 13 5 11 11 11 11 5 13 5 13 11 19 11")
      .append("polygon").attr("points", "0 0 24 0 24 24 0 24")

    let location = d3.select('#container')
      .append("svg")
      .attr("viewBox", [0, 0, 24, 24])
      .attr("focusable", "false")
      .attr("aria-hidden", "true")
      .attr("role", "presentation")
        .attr("class", "carto-reset-zoom-button")
        .on("click", () => svg.transition().duration(750).call(zoom.transform, d3.zoomIdentity.scale(1)))

      location.append("path").attr("fill", "none").attr("d", "M0 0h24v24H0z")
      location.append("path").attr("d", "M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06C6.83 3.52 3.52 6.83 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c4.17-.46 7.48-3.77 7.94-7.94H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z")

  const link = g.append("g")
      .attr("id", "links-container")
      .data(nodes)
      .attr("stroke", "#999")
    .selectAll("line")
      .data(links)
      .join("line");


    link.each(function(d, i) {
      d3.select(this)
        .style("stroke-dasharray", d => {
          if (d.target.data.children) {
            return ("5, 5")
          } else {
            return "none"
          }
        })
        .attr("stroke", d => {
          if (d.target.data.children) {
              return "#333857";
          }
        })
        .attr("stroke-width", d => {
          if (d.target.data.children) {
            return 2;
          } else {
            return 1
          }
        })
    })

const node = g.append("g")
      .attr("id", "nodes-container")
      .attr("stroke-width", 2)
    .selectAll("g")
      .data(nodes)
      .enter()
      .append("g")
      .call(this.drag(simulation));

    node.filter((d, i) => i === 0 || d.data.children)
      .append("circle")
        .attr("id", "node-circle")
        .attr("r", (d, i) => i === 0 ? 114 : 90)
        .attr("fill", (d, i) => i === 0 ? "#333857" : !d.data.children ? "transparent" : "#505D73")
      .append("title").text(d => d.data.name)

      node.filter(d => !d.data.children)
      .append("rect")
        .attr("fill", "#E0E3E9")
        .attr("stroke", "#647592")
        .attr("stroke-width", 1)
        .attr("width", 141)
        .attr("height", 24)
        .append("title").text(d => d.data.name)

      node.append("text")
        .attr("dy", ".35em")
        .style("text-anchor", (d, i) => i === 0 || d.data.children ? "middle" : "left")
        .style("font-size", (d, i) => i === 0 ? "32px" : d.data.children ? '14px' : "12px")
        .style("font-family", (d, i) => i === 0 ? "Roboto Medium" : !d.data.children ? "Roboto" : "Roboto Regular")
        .style("fill", (d, i) => !(i === 0 || d.data.children) ? "#647592" : "#fff")
        .text((d, i) => i === 0 ? leavesNumber + " results" : d.data.name)

  simulation.on("tick", () => {
  // console.log("this.props.data in tick=>", this.props.data)
    let textsWidth = [];

    link
        .attr("x1", d => d.source.x)
        .attr("y1", d => d.source.y)
        .attr("x2", d => d.target.x)
        .attr("y2", d => d.target.y);

    d3.selectAll("#node-circle")
        .attr("cx", d => d.x)
        .attr("cy", d => d.y);

    const texts = d3.selectAll("text")
      // + 10 because + 20 in the final width to make padding
      .attr("x", d => d.data.children ? d.x : d.x + 10)
      // + 12 to center the text in height, rect height is currently 24
      .attr("y", d => d.data.children ? d.y : d.y + 12)

    texts.each(function(d, i) { 
      if (!d.data.children) {
        textsWidth.push({width: this.getBBox().width, name: d.data.name})
      }})

    texts.each(function(d, i) {
      d3.select(this)
        .attr("x", d => {
          if (d.data.children) {
            return d.x
          } else {
            // set text position after rect width has been set
            const currentElement = textsWidth.filter(item => d.data.name === item.name)
            return ((d.x) - (Math.round(currentElement[0].width) / 2))
          }
        })
    })

    d3.selectAll("rect")
      .attr("x", d => d.x)
      .attr("y", d => d.y)
      .each(function(d, i) {
        // re set rect size after txt size has been known
        let currentElement = textsWidth.filter(item => d.data.name === item.name)
        d3.select(this)
          .attr("width", Math.round(currentElement[0].width) + 20)
          .attr("x", d => (d.x) - ((Math.round(currentElement[0].width) + 20) / 2))
          .attr("height", 24)
        })
  });

  // invalidation.then(() => simulation.stop());
    return svg.node();
  }
updateData(data) {
    const root = d3.hierarchy(data);
    const links = root.links();
    const nodes = root.descendants();
    console.log("nodes =>", nodes)
    const leavesNumber = root.leaves().length;
    const initScale = 1 / Math.log(root.descendants().length);
    const initTransX = 1;
    const initTransY = initTransX * initScale;

    const simulation = d3.forceSimulation(nodes)
    .force("link", d3.forceLink(links).id(d => d.id).distance(
      d => {
      if (d.source.parent === null) {
        return 400
      } else {
        return 0
      }
    })
    .strength(1))
    .force("charge", d3.forceManyBody().strength(-50))
    .force("x", d3.forceX())
    .force("y", d3.forceY())
    //forceCollide create a radius around the node which will reject elements entering in
    .force("collide", d3.forceCollide(d => d.depth === 0 ? 400 : d.depth === 1 ? 200 : 100))
    .force('center', d3.forceCenter(0, 50))

    console.log("root 2 =>", root)
    console.log("links 2 =>", links)

    // get main element
    let g = d3.select("#main-g-container")
      .attr("transform","translate("+initTransX+","+initTransY+")scale("+initScale+","+initScale+")")

    d3.select("#main-g-container")
      .join("#main-g-container")
        .attr("transform","translate("+initTransX+","+initTransY+")scale("+initScale+","+initScale+")")

    d3.select("#nodes-container")
      .selectAll("g")
        .data(nodes)
        .join(
          enter => enter.append("g")
            .attr("id", "node-element")
            .call(this.drag(simulation))
            .filter((d, i) => i === 0 || d.data.children)
            .append("circle"),
          update => update,
          exit => exit.remove(),
        )
这里是更新数据的函数(发送到此函数的参数中的数据是我想在图表中显示的新数据):

我要显示和更新的数据=>

const mockData = {
    "name": "Eve",
    "children": [
    {
        "name": "Cain",
        "children": [
            {
                "name": "Ragnar",
            },
            {
                "name": "Freya",
            }
        ]
    },
    {
        "name": "Seth",
        "children": [
        {
            "name": "Enos le castor des prairies",
        },
        {
            "name": "Noam",
        }
    ]
    },
    {
        "name": "Awan",
        "children": [
        {
            "name": "Enoch",
        }
        ]
    },
  ]
}
我目前正在尝试更新svg中的圆圈(首先,在更新其余元素之前)。 到目前为止,我能够创建g元素,并在需要时在其中放置圆。因此,我迷路了,我不知道如何通过.join()给我的圆指定位置,因为我不知道如何选择这些圆。我应该创建一个d3.selectAll(#节点圆)还是选择父节点,然后找到更新其子节点的方法

我看到退出将包含更新集团不会更新的任何元素,因此我理解更新在加入中的重要性,我认为我得到了全球原则;但遗憾的是,我还是太困惑了

我是d3的新手,所以任何建议都将不胜感激

提前感谢,

这可能会有帮助:

在最后有一个链接指向.join()的后续文章,该文章随版本5提供