【问题标题】:Links connected but not visible链接已连接但不可见
【发布时间】:2021-02-26 09:46:47
【问题描述】:

图表正确连接了节点,但链接不可见。我直接在update()函数中添加了.attr("stroke", "black").attr("stroke-width", 2)。在我在 CSS 中使用它之前,它也不起作用。浏览器检查器告诉我,我的链接组的 x、y 位置为 0。这让我很恼火,通常这些位置是在 ticked() 函数中由 source.x,y 和 target.x,y 提供的。

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>D3v4 enter().exit().remove()</title>
    <!-- favcon -->
    <link rel="icon" href="https://networkrepository.com/favicon.png">
    <!-- call external d3.js framework -->
    <script src="https://d3js.org/d3.v4.js"></script>
    <!-- import multiselection framework -->
    <script src="https://d3js.org/d3-selection-multi.v1.js"></script>
    <!-- import "font awesome" stylesheet https://fontawesome.com/ -->
    <script src="https://kit.fontawesome.com/39094309d6.js" crossorigin="anonymous"></script>
</head>

<style>
    body {
        overflow: hidden;
        margin: 0px;
    }

    .canvas {
        background-color: rgb(220, 220, 220);
    }

    .node_group {
        fill: whitesmoke;
        stroke: white;
        stroke-width: 2px
    }

    .node_label {
        stroke: black;
        stroke-width: 0px;
        fill: black
    }

    .tooltip {
        font-family: "Open Sans", sans-serif;
        position: absolute;
        text-align: left;
        background: rgb(245, 245, 245);
        border: 2px;
        border-radius: 6px;
        border-color: rgb(255, 255, 255);
        border-style: solid;
        pointer-events: none;
        line-height: 150%;
        padding: 8px 10px;
    }

    #context-menu {
        font-family: "Open Sans", sans-serif;
        position: fixed;
        z-index: 10000;
        width: 190px;
        background: whitesmoke;
        border: 2px;
        border-radius: 6px;
        border-color: white;
        border-style: solid;
        transform: scale(0);
        transform-origin: top left;
    }

    #context-menu.active {
        transform: scale(1);
        transition: transform 200ms ease-in-out;
    }

    #context-menu .item {
        padding: 8px 10px;
        font-size: 15px;
        color: black;
    }

    #context-menu .item i {
        display: inline-block;
        margin-right: 5px;
    }

    #context-menu hr {
        margin: 5px 0px;
        border-color: whitesmoke;
    }

    #context-menu .item:hover {
        background: lightblue;
    }
</style>

<body>
    <!-- right click context menu -->
    <div id="context-menu">
        <div id="addObject" class="item">
            <i class="fa fa-plus-circle"></i> Add Node
        </div>
        <div id="removeObject" class="item">
            <i class="fa fa-minus-circle"></i> Remove Node
        </div>
    </div>

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


    <!-- call script where the main application is written -->
    <script>
        var graph = {
            "nodes": [{
                "id": 0,
                "name": "Company",
            },
            {
                "id": 1,
                "name": "1",
            },
            {
                "id": 2,
                "name": "2",
            },
            {
                "id": 3,
                "name": "3",
            },
            {
                "id": 4,
                "name": "4",
            }
            ],
            "links": [{
                "source": 1,
                "target": 0,
            },
            {
                "source": 2,
                "target": 0,
            },
            {
                "source": 3,
                "target": 0,
            },
            {
                "source": 4,
                "target": 0,
            },
            ]
        }

        // declare initial variables
        var svg = d3.select("svg")
        width = window.innerWidth
        height = window.innerHeight
        thisNode = null;

        // define cavnas area to draw everything
        svg = d3.select("svg")
            .attr("class", "canvas")
            .attr("width", width)
            .attr("height", height)
            .call(d3.zoom().on("zoom", function () {
                svg.attr("transform", d3.event.transform)
            }))
            .append("g")

        // iniital force simulation
        var simulation = d3.forceSimulation()
            .force("link", d3.forceLink().id(function (d) {
                return d.id;
            }).distance(100))
            .force("charge", d3.forceManyBody().strength(-80))
            .force("center", d3.forceCenter(width / 2, height / 2))
            .force("attraceForce", d3.forceManyBody().strength(70));

        var node_group = null
        var link_group = null

        update()

        /*
        console.log("Initial Nodes")
        console.log(graph.nodes)
        console.log("------------------")
        */

        function update() {
            //define group and join - nodes
            node_group = svg.selectAll(".node_group")
                .data(graph.nodes, d => d.id)

            //exit, remove
            node_group.exit().remove();

            //enter
            var enterN = node_group.enter()
                .append("g").attr("class", "node_group");

            //append
            enterN.append("circle")
                .attr("class", "node_circle")
                .attr("r", 20)
                .on("contextmenu", contextMenu)
               

            enterN.append("text")
                .attr("class", "node_label")
                .text(function (d) {
                    return d.name
                })

            //merge
            node_group = node_group.merge(enterN);

            node_group.call(d3.drag()
                    .on("start", dragStarted)
                    .on("drag", dragged)
                    .on("end", dragEnded)
                )

            //###LINKS

            //define group and join -links
            link_group = svg.selectAll(".link_group")
                .data(graph.links)

            //exit, remove
            link_group.exit().remove();

            //enter
            var enterL = link_group.enter()
                .append("g").attr("class", "link_group")

            //append
            enterL.append("line")
                .attr("class", "link_stroke")
                .attr("stroke", "black")
                .attr("sroke-width", 2)

            //merge
            link_group = link_group.merge(enterL);


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

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

            simulation.alphaTarget(0.3).restart()
        }

        function contextMenu(d) {
            thisNode = d

            event.preventDefault()

            var contextMenu = document.getElementById("context-menu")
            contextMenu.style.top = event.clientY + "px"
            contextMenu.style.left = event.clientX + "px"
            contextMenu.classList.add("active")

            window.addEventListener("click", function () {
                contextMenu.classList.remove("active")
            })

            document.getElementById("addObject").addEventListener("click", addNode)
            document.getElementById("removeObject").addEventListener("click", removeNodeClicked)

        }

        function removeNodeClicked() {
            removeNode(thisNode)
        }

        function addNode() {
            var newID = Math.floor(Math.random() * 100000)

            /*
            console.log("Before adding Node")
            console.log(graph.nodes)
            console.log("------------------")
            */

            graph.links.push({source: newID, target: thisNode.id})

            graph.nodes.push({ id: newID, name: newID })

            /*
            console.log("After adding Node")
            console.log(graph.nodes)
            console.log("------------------")
            */

            update()
        }

        function removeNode(thisNode) {
            var indexOfNode = graph.nodes.indexOf(thisNode)

            var indexOfLink = graph.links.findIndex(element => element.source.id === thisNode.id)

            /*
            console.log("Before removing Node")
            console.log(graph.nodes)
            console.log("------------------")
            */

            graph.links.splice(indexOfLink,1)

            graph.nodes.splice(indexOfNode, 1)

            /*
            console.log("After removing Node")
            console.log(graph.nodes)
            console.log("------------------")
            */

            update()
        }

        function ticked() {
            // update link positions
            link_group
                .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_group
                .attr("transform", function (d) { return "translate(" + d.x + ", " + d.y + ")"; });
        }

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

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

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

    </script>
</body>

</html>

【问题讨论】:

    标签: javascript svg d3.js


    【解决方案1】:

    无论出于何种原因,您都将&lt;line&gt; 附加到&lt;g&gt; 元素中,然后在ticked 函数中将x1y1x2y2 属性设置为那些组,而不是排队。

    也就是说,该代码工作的最小更改是,在 ticked 函数中:

    link_group.select("line")
        .attr("x1", etc...
    

    但是,我会完全放弃这些组,只附加这些行。

    这是您的代码,仅进行了更改:

    <!DOCTYPE html>
    <html>
    
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    
        <title>D3v4 enter().exit().remove()</title>
        <!-- favcon -->
        <link rel="icon" href="https://networkrepository.com/favicon.png">
        <!-- call external d3.js framework -->
        <script src="https://d3js.org/d3.v4.js"></script>
        <!-- import multiselection framework -->
        <script src="https://d3js.org/d3-selection-multi.v1.js"></script>
        <!-- import "font awesome" stylesheet https://fontawesome.com/ -->
        <script src="https://kit.fontawesome.com/39094309d6.js" crossorigin="anonymous"></script>
    </head>
    
    <style>
        body {
            overflow: hidden;
            margin: 0px;
        }
    
        .canvas {
            background-color: rgb(220, 220, 220);
        }
    
        .node_group {
            fill: whitesmoke;
            stroke: white;
            stroke-width: 2px
        }
    
        .node_label {
            stroke: black;
            stroke-width: 0px;
            fill: black
        }
    
        .tooltip {
            font-family: "Open Sans", sans-serif;
            position: absolute;
            text-align: left;
            background: rgb(245, 245, 245);
            border: 2px;
            border-radius: 6px;
            border-color: rgb(255, 255, 255);
            border-style: solid;
            pointer-events: none;
            line-height: 150%;
            padding: 8px 10px;
        }
    
        #context-menu {
            font-family: "Open Sans", sans-serif;
            position: fixed;
            z-index: 10000;
            width: 190px;
            background: whitesmoke;
            border: 2px;
            border-radius: 6px;
            border-color: white;
            border-style: solid;
            transform: scale(0);
            transform-origin: top left;
        }
    
        #context-menu.active {
            transform: scale(1);
            transition: transform 200ms ease-in-out;
        }
    
        #context-menu .item {
            padding: 8px 10px;
            font-size: 15px;
            color: black;
        }
    
        #context-menu .item i {
            display: inline-block;
            margin-right: 5px;
        }
    
        #context-menu hr {
            margin: 5px 0px;
            border-color: whitesmoke;
        }
    
        #context-menu .item:hover {
            background: lightblue;
        }
    </style>
    
    <body>
        <!-- right click context menu -->
        <div id="context-menu">
            <div id="addObject" class="item">
                <i class="fa fa-plus-circle"></i> Add Node
            </div>
            <div id="removeObject" class="item">
                <i class="fa fa-minus-circle"></i> Remove Node
            </div>
        </div>
    
        <svg id="svg"> </svg>
    
    
        <!-- call script where the main application is written -->
        <script>
            var graph = {
                "nodes": [{
                    "id": 0,
                    "name": "Company",
                },
                {
                    "id": 1,
                    "name": "1",
                },
                {
                    "id": 2,
                    "name": "2",
                },
                {
                    "id": 3,
                    "name": "3",
                },
                {
                    "id": 4,
                    "name": "4",
                }
                ],
                "links": [{
                    "source": 1,
                    "target": 0,
                },
                {
                    "source": 2,
                    "target": 0,
                },
                {
                    "source": 3,
                    "target": 0,
                },
                {
                    "source": 4,
                    "target": 0,
                },
                ]
            }
    
            // declare initial variables
            var svg = d3.select("svg")
            width = window.innerWidth
            height = window.innerHeight
            thisNode = null;
    
            // define cavnas area to draw everything
            svg = d3.select("svg")
                .attr("class", "canvas")
                .attr("width", width)
                .attr("height", height)
                .call(d3.zoom().on("zoom", function () {
                    svg.attr("transform", d3.event.transform)
                }))
                .append("g")
    
            // iniital force simulation
            var simulation = d3.forceSimulation()
                .force("link", d3.forceLink().id(function (d) {
                    return d.id;
                }).distance(100))
                .force("charge", d3.forceManyBody().strength(-80))
                .force("center", d3.forceCenter(width / 2, height / 2))
                .force("attraceForce", d3.forceManyBody().strength(70));
    
            var node_group = null
            var link_group = null
    
            update()
    
            /*
            console.log("Initial Nodes")
            console.log(graph.nodes)
            console.log("------------------")
            */
    
            function update() {
                //define group and join - nodes
                node_group = svg.selectAll(".node_group")
                    .data(graph.nodes, d => d.id)
    
                //exit, remove
                node_group.exit().remove();
    
                //enter
                var enterN = node_group.enter()
                    .append("g").attr("class", "node_group");
    
                //append
                enterN.append("circle")
                    .attr("class", "node_circle")
                    .attr("r", 20)
                    .on("contextmenu", contextMenu)
                   
    
                enterN.append("text")
                    .attr("class", "node_label")
                    .text(function (d) {
                        return d.name
                    })
    
                //merge
                node_group = node_group.merge(enterN);
    
                node_group.call(d3.drag()
                        .on("start", dragStarted)
                        .on("drag", dragged)
                        .on("end", dragEnded)
                    )
    
                //###LINKS
    
                //define group and join -links
                link_group = svg.selectAll(".link_group")
                    .data(graph.links)
    
                //exit, remove
                link_group.exit().remove();
    
                //enter
                var enterL = link_group.enter()
                    .append("g").attr("class", "link_group")
    
                //append
                enterL.append("line")
                    .attr("class", "link_stroke")
                    .attr("stroke", "black")
                    .attr("sroke-width", 2)
    
                //merge
                link_group = link_group.merge(enterL);
    
    
                simulation
                    .nodes(graph.nodes)
                    .on("tick", ticked);
    
                simulation
                    .force("link")
                    .links(graph.links)
    
                simulation.alphaTarget(0.3).restart()
            }
    
            function contextMenu(d) {
                thisNode = d
    
                event.preventDefault()
    
                var contextMenu = document.getElementById("context-menu")
                contextMenu.style.top = event.clientY + "px"
                contextMenu.style.left = event.clientX + "px"
                contextMenu.classList.add("active")
    
                window.addEventListener("click", function () {
                    contextMenu.classList.remove("active")
                })
    
                document.getElementById("addObject").addEventListener("click", addNode)
                document.getElementById("removeObject").addEventListener("click", removeNodeClicked)
    
            }
    
            function removeNodeClicked() {
                removeNode(thisNode)
            }
    
            function addNode() {
                var newID = Math.floor(Math.random() * 100000)
    
                /*
                console.log("Before adding Node")
                console.log(graph.nodes)
                console.log("------------------")
                */
    
                graph.links.push({source: newID, target: thisNode.id})
    
                graph.nodes.push({ id: newID, name: newID })
    
                /*
                console.log("After adding Node")
                console.log(graph.nodes)
                console.log("------------------")
                */
    
                update()
            }
    
            function removeNode(thisNode) {
                var indexOfNode = graph.nodes.indexOf(thisNode)
    
                var indexOfLink = graph.links.findIndex(element => element.source.id === thisNode.id)
    
                /*
                console.log("Before removing Node")
                console.log(graph.nodes)
                console.log("------------------")
                */
    
                graph.links.splice(indexOfLink,1)
    
                graph.nodes.splice(indexOfNode, 1)
    
                /*
                console.log("After removing Node")
                console.log(graph.nodes)
                console.log("------------------")
                */
    
                update()
            }
    
            function ticked() {
                // update link positions
                link_group.select("line")
                    .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_group
                    .attr("transform", function (d) { return "translate(" + d.x + ", " + d.y + ")"; });
            }
    
            function dragStarted(d) {
                if (!d3.event.active) simulation.alphaTarget(0.3).restart();
                d.fx = d.x;
                d.fy = d.y;
            }
    
            function dragged(d) {
                d.fx = d3.event.x;
                d.fy = d3.event.y;
            }
    
            function dragEnded(d) {
                if (!d3.event.active) simulation.alphaTarget(0);
                d.fx = undefined;
                d.fy = undefined;
            }
    
        </script>
    </body>
    
    </html>

    【讨论】:

    • 我找到了你,但我不明白为什么它不适用于小组。我的主要代码包含每个链接、文本路径和标签。我想把所有东西都放在一个巨大的背包里,然后用 ticked() 重新分配这个背包。它也适用于节点,因为我得到了节点组本身以及附加的圆圈和文本。仍然让我感到困惑。
    • 它适用于节点,因为组具有 transform 作为表示属性。正如我所说,您不需要链接组,因此只需为这些行创建 enter-exit-update 选择并在 ticked 函数中更改它们的位置。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-13
    • 2020-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-28
    相关资源
    最近更新 更多