【问题标题】:D3 exit() update() merge() issueD3 exit() update() merge() 问题
【发布时间】:2023-02-21 12:12:39
【问题描述】:

我试图通过简单地点击它来改变每条红线的外观。单击应更改标签和行。根据当前的笔画类型,线条应该更改为清晰的“笔画”或“笔画-dasharray”。同样的点击也会改变在“dasharray”或“clear”之间切换的标签。

我以为我终于理解了 D3 exit()。消除()。 merge() 模式,但似乎我错了。这次我不想更改节点或链接的数量。我指的是我想动态更改的属性。

据我了解,LinkLines 和“节点”本身都被“重绘”了。但我只想更新当前的文本和外观。

我感谢任何提示。

        var data = {
            "nodes": [{
                "id": 1
            },
            {
                "id": 2,
            },
            {
                "id": 3,
            }],
            "links": [{
                "source": 1,
                "target": 2,
                "text": "dashed"
            },
            {
                "source": 2,
                "target": 3,
                "text": "clear"
            },
            {
                "source": 3,
                "target": 1,
                "text": "clear"
            }
            ]
        };

        let nodes = data.nodes
        let links = data.links

        //Helper
        let nodeToDelete

        var width = window.innerWidth,
            height = window.innerHeight;

        var svg = d3.select("body").append("svg")
            .attr("width", width)
            .attr("height", height)
            .call(d3.zoom().on("zoom", function (event) {
                svg.attr("transform", event.transform)
            }))
            .append("g")

        var simulation = d3.forceSimulation()
            .force("size", d3.forceCenter(width / 2, height / 2))
            .force("charge", d3.forceManyBody().strength(-5000))
            .force("link", d3.forceLink().id(function (d) {
                return d.id
            }).distance(250))


        linksContainer = svg.append("g").attr("class", "linkscontainer")
        nodesContainer = svg.append("g").attr("class", "nodesContainer")

        restart()

        simulation
            .nodes(nodes)
            .on("tick", tick)

        simulation
            .force("link").links(links)

        function tick() {
            linkLine.attr("d", function (d) {
                var dx = (d.target.x - d.source.x),
                    dy = (d.target.y - d.source.y),
                    dr = Math.sqrt(dx * dx + dy * dy)

                return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
            })

            node
                .attr("transform", d => `translate(${d.x}, ${d.y})`);
        }

        function dragStarted(event, d) {
            if (!event.active) simulation.alphaTarget(0.3).restart();
            d.fx = d.x;
            d.fy = d.y;
        }

        function dragged(event, d) {
            d.fx = event.x;
            d.fy = event.y;
        }

        function dragEnded(event, d) {
            if (!event.active) simulation.alphaTarget(0);
            d.fx = null;
            d.fy = null;
        }


        function restart() {

            // Update linkLines
            linkLine = linksContainer.selectAll(".linkPath")
                .data(links, link => link.text) // ADD DATA KEY FOR LINK

            linkLine.exit().remove()

            const linkLineEnter = linkLine.enter()
                .append("path")
                .attr("class", "linkPath")
                .attr("class", function(d) {
                    if (d.text != "dashed") {
                        return "linkpath clearstroke"
                    } else if (d.text == "dashed") {
                        return "linkpath dasharray"
                    }
                })
                .attr("fill", "transparent")
                .attr("id", function (_, i) {
                    return "path" + i
                })
                .attr("cursor", "pointer")
                .on("click", function(_, d) {

                    let linkIndexOf = links.indexOf(d)

                    if (links[linkIndexOf].text == "clear") {
                        links[linkIndexOf].text = "dashed"
                    } else if (links[linkIndexOf].text == "dashed") {
                        links[linkIndexOf].text = "clear"
                    }

                    console.log(links)

                    restart()
                })

            linkLine = linkLineEnter.merge(linkLine)

            // Update linkText
            linkText = linksContainer.selectAll(".linkLabel") // FIXED ClassName
                .data(links, link => link.text)  // ADD DATA KEY FOR TEXT

            linkText.exit().remove()

            const linkTextEnter = linkText.enter()
                .append("text")
                .attr("dy", -10)
                .attr("class", "linkLabel")
                .attr("id", function (d, i) { return "linkLabel" + i })
                .attr("text-anchor", "middle")
                .text("")

            linkTextEnter.append("textPath")
                .attr("xlink:href", function (_, i) {
                    return "#path" + i
                })
                .attr("startOffset", "50%")
                .attr("opacity", 0.75)
                .attr("cursor", "default")
                .attr("class", "linkText")
                .attr("color", "black")
                .text(function (d) {
                    return d.text
                })

            linkText = linkTextEnter.merge(linkText)

            // Update nodes
            node = nodesContainer.selectAll(".nodes")
                .data(nodes, node => node.id) // ADD DATA KEY FOR NODE

            node.exit().remove()

            const nodesEnter = node.enter()
                .append("g")
                .attr("class", "nodes")
                .call(d3.drag()
                    .on("start", dragStarted)
                    .on("drag", dragged)
                    .on("end", dragEnded)
                )

            nodesEnter.selectAll("circle")
                .data(d => [d])
                .enter()
                .append("circle")
                .style("stroke", "blue")
                .attr("r", 40)

            nodesEnter.append("text")
                .attr("dominant-baseline", "central")
                .attr("text-anchor", "middle")
                .attr("font-size", 20)
                .attr("fill", "black")
                .attr("pointer-events", "none")
                .text(function (d) {
                    return d.id
                })

            node = nodesEnter.merge(node)

            // Update and restart the simulation.
            simulation
                .nodes(nodes);
            simulation
                .force("link")
                .links(links)
            simulation.restart().alpha(1)
        }
    .clearstroke {
        stroke: red;
        stroke-width: 2px;
    }

    .dasharray {
        stroke: red;
        stroke-width: 2px;
        stroke-dasharray: 5;
    }
    
    .nodes {
        fill: whitesmoke;
     }
<!DOCTYPE html>
<html lang="de">

<head>
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
    <meta charset="utf-8">

    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-3.6.3.js"></script>
    <!-- D3 -->
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <!-- fontawesome stylesheet https://fontawesome.com/ -->
    <script src="https://kit.fontawesome.com/98a5e27706.js" crossorigin="anonymous"></script>
</head>

<body>
   

</body>

</html>

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    您的代码中有几个问题,应该重构。但是,仅解决您的主要投诉,存在两个问题:

    1. 键(在键功能中)应该是独特的.如您所见,link.text 不是唯一的,但 link.source + link.target 是。

    2. 类名是linkPath,而不是linkpath

      这是您进行这些更改的代码:

      var data = {
        "nodes": [{
            "id": 1
          },
          {
            "id": 2,
          },
          {
            "id": 3,
          }
        ],
        "links": [{
            "source": 1,
            "target": 2,
            "text": "dashed"
          },
          {
            "source": 2,
            "target": 3,
            "text": "clear"
          },
          {
            "source": 3,
            "target": 1,
            "text": "clear"
          }
        ]
      };
      
      let nodes = data.nodes
      let links = data.links
      
      //Helper
      let nodeToDelete
      
      var width = window.innerWidth,
        height = window.innerHeight;
      
      var svg = d3.select("body").append("svg")
        .attr("width", width)
        .attr("height", height)
        .call(d3.zoom().on("zoom", function(event) {
          svg.attr("transform", event.transform)
        }))
        .append("g")
      
      var simulation = d3.forceSimulation()
        .force("size", d3.forceCenter(width / 2, height / 2))
        .force("charge", d3.forceManyBody().strength(-5000))
        .force("link", d3.forceLink().id(function(d) {
          return d.id
        }).distance(250))
      
      
      linksContainer = svg.append("g").attr("class", "linkscontainer")
      nodesContainer = svg.append("g").attr("class", "nodesContainer")
      
      restart()
      
      simulation
        .nodes(nodes)
        .on("tick", tick)
      
      simulation
        .force("link").links(links)
      
      function tick() {
        linkLine.attr("d", function(d) {
          var dx = (d.target.x - d.source.x),
            dy = (d.target.y - d.source.y),
            dr = Math.sqrt(dx * dx + dy * dy)
      
          return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
        })
      
        node
          .attr("transform", d => `translate(${d.x}, ${d.y})`);
      }
      
      function dragStarted(event, d) {
        if (!event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
      }
      
      function dragged(event, d) {
        d.fx = event.x;
        d.fy = event.y;
      }
      
      function dragEnded(event, d) {
        if (!event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
      }
      
      
      function restart() {
      
        // Update linkLines
        linkLine = linksContainer.selectAll(".linkPath")
          .data(links, link => link.source + link.target) // ADD DATA KEY FOR LINK
      
        linkLine.exit().remove()
      
        const linkLineEnter = linkLine.enter()
          .append("path")
          .attr("class", "linkPath")
          .attr("class", function(d) {
            if (d.text != "dashed") {
              return "linkPath clearstroke"
            } else if (d.text == "dashed") {
              return "linkPath dasharray"
            }
          })
          .attr("fill", "transparent")
          .attr("id", function(_, i) {
            return "path" + i
          })
          .attr("cursor", "pointer");
      
        linkLine = linkLineEnter.merge(linkLine)
      
        linkLine.on("click", function(_, d) {
      
          let linkIndexOf = links.indexOf(d)
      
          if (links[linkIndexOf].text == "clear") {
            links[linkIndexOf].text = "dashed"
          } else if (links[linkIndexOf].text == "dashed") {
            links[linkIndexOf].text = "clear"
          }
      
          restart()
        })
      
        // Update linkText
        linkText = linksContainer.selectAll(".linkLabel") // FIXED ClassName
          .data(links, link => link.source + link.target) // ADD DATA KEY FOR TEXT
      
        linkText.exit().remove()
      
        const linkTextEnter = linkText.enter()
          .append("text")
          .attr("dy", -10)
          .attr("class", "linkLabel")
          .attr("id", function(d, i) {
            return "linkLabel" + i
          })
          .attr("text-anchor", "middle")
          .text("")
      
        linkTextEnter.append("textPath")
          .attr("xlink:href", function(_, i) {
            return "#path" + i
          })
          .attr("startOffset", "50%")
          .attr("opacity", 0.75)
          .attr("cursor", "default")
          .attr("class", "linkText")
          .attr("color", "black")
          .text(function(d) {
            return d.text
          })
      
        linkText = linkTextEnter.merge(linkText)
      
        // Update nodes
        node = nodesContainer.selectAll(".nodes")
          .data(nodes, node => node.id) // ADD DATA KEY FOR NODE
      
        node.exit().remove()
      
        const nodesEnter = node.enter()
          .append("g")
          .attr("class", "nodes")
          .call(d3.drag()
            .on("start", dragStarted)
            .on("drag", dragged)
            .on("end", dragEnded)
          )
      
        nodesEnter.selectAll("circle")
          .data(d => [d])
          .enter()
          .append("circle")
          .style("stroke", "blue")
          .attr("r", 40)
      
        nodesEnter.append("text")
          .attr("dominant-baseline", "central")
          .attr("text-anchor", "middle")
          .attr("font-size", 20)
          .attr("fill", "black")
          .attr("pointer-events", "none")
          .text(function(d) {
            return d.id
          })
      
        node = nodesEnter.merge(node)
      
        // Update and restart the simulation.
        simulation
          .nodes(nodes);
        simulation
          .force("link")
          .links(links)
        simulation.restart().alpha(1)
      }
      .clearstroke {
        stroke: red;
        stroke-width: 2px;
      }
      
      .dasharray {
        stroke: red;
        stroke-width: 2px;
        stroke-dasharray: 5;
      }
      
      .nodes {
        fill: whitesmoke;
      }
      <!DOCTYPE html>
      <html lang="de">
      
      <head>
        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
        <meta charset="utf-8">
      
        <!-- jQuery -->
        <script src="https://code.jquery.com/jquery-3.6.3.js"></script>
        <!-- D3 -->
        <script src="https://d3js.org/d3.v7.min.js"></script>
        <!-- fontawesome stylesheet https://fontawesome.com/ -->
        <script src="https://kit.fontawesome.com/98a5e27706.js" crossorigin="anonymous"></script>
      </head>
      
      <body>
      
      
      </body>
      
      </html>

    【讨论】:

      猜你喜欢
      • 2018-04-14
      • 2020-07-08
      • 2023-03-05
      • 2020-04-12
      • 2021-04-12
      • 2014-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多