簡體   English   中英

D3 在力有向圖中添加節點,如何在 v5(vs. v3)中

[英]D3 adding nodes in force-directed graph, how to in v5 (vs. v3)

我發現了一個很好的 FD Graph 腳本,可以交互地添加和刪除節點,用 D3 v3 編寫。 (見http://bl.ocks.org/tgk/6068367 )。 我試圖將它移植到 v5,但它不起作用。 我構建了以下腳本來比較兩個版本。 我發現的任何示例,只需切換鏈接數據(參見https://bl.ocks.org/colbenkharrl/21b3808492b93a21de841bc5ceac4e47 ),而不是節點數據。

任何人都可以幫忙嗎?

切換D3版本請看代碼第34行!

<!DOCTYPE html>
<meta charset="utf-8">
<style>
    html {
        background-color: black;
    }

    svg {
        background-color: white;
    }

    rect {
        fill: none;
        pointer-events: all;
    }

    .node {
        fill: #000;
    }

    .cursor {
        fill: none;
        stroke: brown;
        pointer-events: none;
    }

    .link {
        stroke: #999;
    }
</style>

<body>
    <script>
        let version = 5;
        let loader = 'document.write("<' + 'script src=\\"https://d3js.org/d3.v' + version + '.min.js\\"></' +
            'script>' +
            '");';
        eval(loader);
    </script>
    <script>
        !(function () {
            'use strict';
            // - - - - - - - - - -
            // 1. Global declaration
            // - - - - - - - - - -
            let
                fill, simulation, svg, nodes, links, node, link, cursor,
                i = false,
                canvas = {},
                dataset = {};

            // Starting with an empty dataset
            dataset.nodes = [{}];
            dataset.links = [{}];

            canvas = {
                width: 800,
                height: 400
            };

            // - - - - - - - - - -
            // 2. Functions
            // - - - - - - - - - -
            // 2.1. Eventhandler functions
            // - - - - - - - - - -
            function onCanvasMousemove() {
                cursor
                    .attr("transform", "translate(" + d3.mouse(this) + ")");
            }

            function onCanvasMousedown() {
                let point = d3.mouse(this),
                    node = {
                        x: point[0],
                        y: point[1]
                    },
                    n = nodes.push(node);

                // add links to any nearby nodes
                nodes
                    .forEach(function (target) {
                        let x = target.x - node.x,
                            y = target.y - node.y;
                        if (Math.sqrt(x * x + y * y) < 30) {
                            links.push({
                                source: node,
                                target: target
                            });
                        }
                    });
                restartSimulation();
            }

            function onNodeMousedown(d, i) {
                nodes.splice(i, 1);
                links = links.filter(function (l) {
                    return l.source !==
                        d && l.target !== d;
                });
                d3.event.stopPropagation();
                restartSimulation();
            }

            function onTicked() {
                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;
                    });

                node
                    .attr("cx", function (d) {
                        if (i === false) {
                            console.log(typeof node);
                            console.log(typeof d);
                            i = true;
                        }
                        return d.x;
                    }).attr("cy", function (d) {
                        return d.y;
                    });
            }

            // - - - - - - - - - - 
            // 2.2 Setter functions 
            // - - - - - - - - - - 
            function setSvg() {
                svg = d3.select("body")
                    .append("svg")
                    .attr("width", canvas.width)
                    .attr("height", canvas.height)
                    .on("mousemove", onCanvasMousemove)
                    .on("mousedown", onCanvasMousedown);

                svg.append("rect")
                    .attr("width", canvas.width)
                    .attr("height", canvas.height);
            }

            function setCursor() {
                // create the cursor 
                cursor = svg.append("circle")
                    .attr("r", 30)
                    .attr("transform", "translate(-100,-100)")
                    .attr("class", "cursor");
            }

            function setColorScheme() {
                switch (version) {
                    case 3:
                        fill = d3.scale.category20();
                        break;
                    case 5:
                        d3.scaleOrdinal(d3.schemeCategory10);
                        break;
                }
            }

            /** Create the force direction graph
             */
            function setSimulation() {
                switch (version) {
                    case 3:
                        simulation = d3.layout
                            .force()
                            .size([canvas.width, canvas.height])
                            .nodes(dataset.nodes) // ! data, initialize with a single node 
                            .linkDistance(30)
                            .charge(-60)
                            .on("tick", onTicked);
                        break;
                    case 4:
                    case 5:
                        simulation = d3.forceSimulation()
                            .nodes(dataset.nodes)
                            .force('link', d3.forceLink().distance(30))
                            .force('charge',
                                d3.forceManyBody()
                                .strength(-60))
                            .force('center', d3.forceCenter(canvas.width / 2, canvas.height / 2))
                            .on('tick', onTicked);
                        break;
                }

                restartSimulation();
            }

            function restartSimulation() {
                // - - - - - - - - - -
                // NODES
                // - - - - - - - - - -
                switch (version) {
                    case 3:
                        node = svg.selectAll('.node');
                        nodes = simulation.nodes();
                        node = node.data(nodes);

                        node.exit().remove();

                        node.enter()
                            .insert("circle", ".cursor")
                            .attr("class", "node")
                            .attr("r", 5)
                            .on("mousedown", onNodeMousedown);
                        break;
                    case 4:
                    case 5:
                        node = svg.selectAll('.node');
                        nodes = simulation.nodes();
                        node = node.data(nodes);

                        node.exit().remove();

                        node.enter()
                            .insert("circle", ".cursor")
                            .attr("class", "node")
                            .attr("r", 5)
                            .merge(node)
                            .on("mousedown", onNodeMousedown);
                        break;
                }

                // - - - - - - - - - -
                // LINKS
                // - - - - - - - - - -
                switch (version) {
                    case 3:
                        link = svg.selectAll('.link');
                        links = simulation.links();
                        link = link.data(links);

                        link.exit().remove();

                        link.enter()
                            .insert("line", ".node")
                            .attr("class", "link")

                        break;
                    case 4:
                    case 5:
                        links = svg.selectAll('.link')
                        link = link.data(links);

                        link.exit().remove();

                        link.enter()
                            .append("line", ".node")
                            .attr("class", "link")
                            .merge(link);
                        break;
                }

                switch (version) {
                    case
                    3:
                        simulation.start();
                        break;
                    case 5:
                        simulation.alphaTarget(0.3).restart();
                        break;
                }
            }
            // - - - - - - - - - - 
            // 2.3 Control functions 
            // - - - - - - - - - - 
            function main() {
                setSvg();
                setSimulation();
                setCursor();
                setColorScheme();
            }

            function init() {
                main();
            };
            // - - - - - - - - - - 
            // 3. Main control 
            // - - - - - - - - - -
            window.addEventListener('load', init);
            // - - - - - - - - - - 
        }())
    </script>

好,我知道了。 關鍵的注釋是https://bl.ocks.org/tezzutezzu/cd04b3f1efee4186ff42aae66c87d1a7

有很多帶有變量的微小重構,隨意區分前后。 在我的第一個版本中刪除 v5->merge() 並清除它適用於 D3v3 和 v5 的變量后。 並且沒有必要使用不同的數據集,正如我首先想到的那樣,一個就足夠了。 另一件事是寫

nodeElements = svg.selectAll(".node").data(dataset.nodes, function (d) {
   return d.id
});

代替

nodeElements = svg.selectAll(".node");
nodeElements.data(dataset.nodes, function (d) {
   return d.id
});

到目前為止,這是代碼。 我還更新了 codepen 示例。

<!DOCTYPE html>
<meta charset="utf-8">
<style>
    html {
        background-color: black;
    }

    svg {
        background-color: white;
    }

    rect {
        fill: none;
        pointer-events: all;
    }

    .node {
        fill: #000;
    }

    .cursor {
        fill: none;
        stroke: brown;
        pointer-events: none;
    }

    .link {
        stroke: #999;
    }
</style>

<body>
    <script>
        let version = 5;
        let loader = 'document.write("<' + 'script src=\\"https://d3js.org/d3.v' + version + '.min.js\\"></' +
            'script>' +
            '");';
        eval(loader);
    </script>
    <script>
        !(function () {
            'use strict';
            // - - - - - - - - - -
            // 1. Global declaration
            // - - - - - - - - - -
            let
                fill, simulation, svg, nodes, links, node, link, cursor,
                nodeElements, linkElements,
                i = false,
                canvas = {},
                dataset = {};

            dataset.nodes = [];
            dataset.links = [];

            canvas = {
                width: 800,
                height: 400
            };

            // - - - - - - - - - -
            // 2. Functions
            // - - - - - - - - - -
            // 2.1. Eventhandler functions
            // - - - - - - - - - -
            function onCanvasMousemove() {
                cursor
                    .attr("transform", "translate(" + d3.mouse(this) + ")");
            }

            function onCanvasMousedown() {
                let
                    point = d3.mouse(this),
                    node = {
                        id: String.fromCharCode(Math.floor(Math.random() * 24) + 65),
                        x: point[0],
                        y: point[1]
                    };

                dataset.nodes.push(node);

                // add links to any nearby nodes
                dataset.nodes
                    .forEach(function (target) {
                        let x = target.x - node.x,
                            y = target.y - node.y;
                        if (Math.sqrt(x * x + y * y) < 30) {
                            dataset.links.push({
                                source: node,
                                target: target
                            });
                        }
                    });

                startSimulation();
            }

            function onNodeMousedown(d, i) {
                dataset.nodes.splice(i, 1);

                dataset.links = dataset.links.filter(function (l) {
                    return l.source !==
                        d && l.target !== d;
                });

                d3.event.stopPropagation();

                startSimulation();
            }

            function onTicked() {
                let nodeElements = svg.selectAll('.node');
                let linkElements = svg.selectAll('.link');

                linkElements
                    .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;
                    });

                nodeElements
                    .attr("cx", function (d) {
                        if (i === false) {
                            console.log(typeof node);
                            console.log(typeof d);
                            i = true;
                        }
                        return d.x;
                    }).attr("cy", function (d) {
                        return d.y;
                    });
            }

            // - - - - - - - - - - 
            // 2.2 Setter functions 
            // - - - - - - - - - - 
            function setSvg() {
                svg = d3.select("body")
                    .append("svg")
                    .attr("width", canvas.width)
                    .attr("height", canvas.height)
                    .on("mousemove", onCanvasMousemove)
                    .on("mousedown", onCanvasMousedown);

                svg.append("rect")
                    .attr("width", canvas.width)
                    .attr("height", canvas.height);
            }

            function setCursor() {
                // create the cursor 
                cursor = svg.append("circle")
                    .attr("r", 30)
                    .attr("transform", "translate(-100,-100)")
                    .attr("class", "cursor");
            }

            function setColorScheme() {
                switch (version) {
                    case 3:
                        fill = d3.scale.category20();
                        break;
                    case 5:
                        d3.scaleOrdinal(d3.schemeCategory10);
                        break;
                }
            }

            /** Create the force direction graph
             */
            function setSimulation() {
                switch (version) {
                    case 3:
                        simulation = d3.layout
                            .force()
                            .size([canvas.width, canvas.height])
                            .nodes(dataset.nodes) // ! data, initialize with a single node 
                            .linkDistance(30)
                            .charge(-60)
                            .on("tick", onTicked);
                        break;
                    case 4:
                    case 5:
                        simulation = d3.forceSimulation()
                            // .nodes(dataset.nodes)
                            .force('link', d3.forceLink()
                                .distance(200)
                                .strength(0.6)
                            )
                            .force('charge',
                                d3.forceManyBody()
                                .strength(-60)
                            )
                            .force("x", d3.forceX(canvas.width / 2))
                            .force("y", d3.forceY(canvas.height / 2))
                            // .force('center', d3.forceCenter(canvas.width / 2, canvas.height / 2))
                            .on('tick', onTicked);
                        break;
                }

                startSimulation();
            }

            function startSimulation() {
                // - - - - - - - - - -
                // NODES
                // - - - - - - - - - -
                switch (version) {
                    case 3:
                        nodeElements = svg.selectAll('.node');
                        dataset.nodes = simulation.nodes();
                        node = nodeElements.data(dataset.nodes);

                        node.exit().remove();

                        node.enter()
                            .insert("circle", ".cursor")
                            .attr("class", "node")
                            .attr("r", 5)
                            .on("mousedown", onNodeMousedown);
                        break;
                    case 4:
                    case 5:
                        nodeElements = svg.selectAll(".node")
                            .data(dataset.nodes, function (d) {
                                return d.id
                            });

                        nodeElements.enter()
                            .append("circle")
                            .attr("class", function (d) {
                                return "node " + d.id;
                            })
                            .attr("r", 8)
                            .on("mousedown", onNodeMousedown);

                        nodeElements.exit().remove();

                        simulation.nodes(dataset.nodes)
                        simulation.force("link").links(dataset.links)
                        simulation.restart();

                        break;
                }

                // - - - - - - - - - -
                // LINKS
                // - - - - - - - - - -
                switch (version) {
                    case 3:
                        link = svg.selectAll('.link');
                        links = simulation.links();
                        link = link.data(links);

                        link.exit().remove();

                        link.enter()
                            .insert("line", ".node")
                            .attr("class", "link")

                        break;
                    case 4:
                    case 5:
                        linkElements = svg
                            .selectAll('.link')
                            .data(dataset.links);

                        linkElements.exit().remove();

                        linkElements.enter()
                            .append("line", ".node")
                            .attr("class", "link");
                        break;
                }

                switch (version) {
                    case
                    3:
                        simulation.start();
                        break;
                    case 5:
                        simulation.alphaTarget(0.3).restart();
                        break;
                }
            }
            // - - - - - - - - - - 
            // 2.3 Control functions 
            // - - - - - - - - - - 
            function main() {
                setSvg();
                setSimulation();
                setCursor();
                setColorScheme();
            }

            function init() {
                main();
            };
            // - - - - - - - - - - 
            // 3. Main control 
            // - - - - - - - - - -
            window.addEventListener('load', init);
            // - - - - - - - - - - 
        }())
    </script>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM