D3 过时链接仍然可见

D3 outdated links still visible

我得到的是:

我得到了一个 D3 强制图,有两种不同的 link 类型。我确实以不同的方式想象这两种类型。 need 是一条简单的线,uses 是虚线。为此,我为 link 设置了两个不同的 CSS 类,如果单击,只需切换类型。为了最终可视化此更改,我再次调用主 initialize() 函数。

问题是什么:

只要我点击其中一个 link 并切换类型,过时的行仍然可见。我错过了如何避免这种行为的重点?我如何确定过时的线路已经消失?我很感激任何提示。

更新:

我在重新初始化之前添加了 svg.selectAll("line").remove()。但我怀疑它的最佳做法,而且有时线条会完全消失。

 var graph = {
            "nodes": [
                {
                    "id": 1
                },
                {
                    "id": 2
                },
                {
                    "id": 3
                }
            ],
            "links": [
                {
                    "source": 1,
                    "target": 2,
                    "type": "uses"
                },
                {
                    "source": 2,
                    "target": 3,
                    "type": "needs"
                },
                {
                    "source": 3,
                    "target": 1,
                    "type": "needs"
                }

            ]
        }

        var svg = d3.select("svg")
            .attr("width", window.innerWidth)
            .attr("height", window.innerHeight)

        var force = d3.forceSimulation()
            .force("link", d3.forceLink().id(function (d) {
                return d.id
            }).distance(80))
            .force("charge", d3.forceManyBody().strength(-100))
            .force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2))
            .force("collision", d3.forceCollide().radius(90))

        initialize()

        function initialize() {

            link = svg.selectAll(".link")
                .data(graph.links)
                .join("line")
                //.attr("class", "link")
                .attr("class", function (d) {
                    if (d.type === "uses") {
                        return "uses"
                    } else {
                        return "needs"
                    }
                })
                .on("dblclick", function (event, d) {
                    if (d.type === "uses") {
                        d.type = "needs"
                    } else if (d.type === "needs") {
                        d.type = "uses"
                    }
                    svg.selectAll("line").remove()
                    initialize()
                })
    
            node = svg.selectAll(".node")
                .data(graph.nodes, d => d.id)
                .join("g")
                .attr("class", "node")
                .call(d3.drag()
                    .on("start", dragStarted)
                    .on("drag", dragged)
                    .on("end", dragEnded)
                )

            node.selectAll("circle")
                .data(graph.nodes)
                .join("circle")
                .attr("r", 30)
                .style("fill", "whitesmoke")

            force
                .nodes(graph.nodes)
                .on("tick", ticked);

            force
                .force("link")
                .links(graph.links)
        }

        function ticked() {
            // update link positions
            link
                .attr("x1", function (d) {
                    return d.source.x;
                })
                .attr("y1", function (d) {
                    return d.source.y;
                })
                .attr("x2", function (d) {
                    return d.target.x;
                })
                .attr("y2", function (d) {
                    return d.target.y;
                });

            // update node positions
            node
                .attr("transform", function (d) {
                    return "translate(" + d.x + ", " + d.y + ")";
                });
        }

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

            PosX = d.x
            PosY = d.y
        }

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

        function dragEnded(event, d) {
            if (!event.active) force.alphaTarget(0);
            d.fx = undefined;
            d.fy = undefined;
        }
  body {
        height: 100%;
        background: #e6e7ee;
        overflow: hidden;
        margin: 0px;
    }

    line {
        stroke-width: 6px;
    }
    
    line.uses {
        stroke: grey;
        stroke-dasharray: 5;
    }

    line.needs {
        stroke: black;
    }

    line:hover {
        stroke: goldenrod;
    }
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- d3.js framework -->
    <script src="https://d3js.org/d3.v6.js"></script>
    
</head>

<body>
    <svg id="svg"></svg>
</body>

</html>

在细分中:

  link = svg.selectAll(".link")
            .data(graph.links)
            .join("line")
            //.attr("class", "link")
            .attr("class", function (d) {
                if (d.type === "uses") {
                    return "uses"
                } else {
                    return "needs"
                }
            })

selection 是 selecting link class,但这些行实际上有 usesneeds 作为 classes。您可以 select 以前的 usesneeds classes:

link = svg.selectAll("line.uses,line.needs")

这将使 .join() 删除那些 classes 中未使用的行。