简体   繁体   English

D3 强制定向布局+所有节点到原始位置

[英]D3 Force directed layout + All nodes to original positions

I have created some basic nodes and links to create a force directed graph with D3.我已经创建了一些基本节点和链接,以使用 D3 创建一个力有向图。 I have set some basic logic on my drag functions so that when a node is dragged it returns to its original starting position.我在拖动函数上设置了一些基本逻辑,以便在拖动节点时它会返回到其原始起始位置。 This worked nicely until I linked them together.这很好用,直到我将它们连接在一起。 How do I get all of the nodes in the chain to return to their original positions after the dragend?如何让链中的所有节点在拖动结束后返回其原始位置?

<!DOCTYPE html>
<meta charset="utf-8">
<style>

    .Properties{
        fill: yellow;
        stroke: black;
        stroke-width: 2px;
    }
    .link {
    stroke: #777;
    stroke-width: 2px;
    }


</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
        //Most of these variables are just used to calculate original position
        var width = 960, height = 500, colors = d3.scale.category10();
        var svg = null, force = null;
        var circle = null, path = null;
        var nodes = null, links = null;
        var nodesArray = null, linkArray = null;
        var count = 0;
        var element = "body"; var numEdges = 4, numNodes = 5;
        var i = 0; var L = 16, r = 12, lineLimit = 10;
        var d = 2 * r + L;
        var R = (count - 1) * d;
        var m = width / 2;
        var X;

        svg = d3.selectAll(element).append('svg').attr('width', width).attr('height', height);

        //Load nodes and links original positions
        nodes = d3.range(numNodes).map(function () {
            X = m - (R / 2) + (i * d);
            ++i;
            return {
                x: X,
                y: (height) / 3,
                fx: X,
                fy: height / 3,
                id: i-1,
                reflexive: true,
                r: 12
            };           
        });
        for (var i = 0; i < numNodes; ++i) {
            d3.select(element).append("h3").text("Node " + i + ": " + nodes[i].id);
        }

        i = -1;
        links = d3.range(numEdges).map(function () {
            i++;
            return {
                //
                source: nodes[i],
                target: nodes[i+1],
                left: false,
                right: true
            }
        });
        for (var i = 0; i < numEdges; ++i) {
            d3.select(element).append("h3").text("Source: " + links[i].source.id + " Target: " + links[i].target.id);
        }

        force = d3.layout.force().size([width, height]).nodes(nodes).links(links).linkDistance(40).linkStrength(0.1).charge(-300);
        var drag = force.drag()
                    .on('dragstart', dragstart)
                    .on('drag', drag)
                    .on('dragend', dragend);
        linkArray = svg.selectAll('.link').data(links).enter().append('line').attr('class', 'link')
            .attr('x1', function (d) {
                return nodes[d.source.id].x;
            })
            .attr('y1', function (d) { return nodes[d.source.id].y; })
            .attr('x2', function (d) { return nodes[d.target.id].x; })
            .attr('y2', function (d) { return nodes[d.target.id].y; });


        var circleGroup = svg.selectAll("g").data(nodes);
        var groupEnter = circleGroup.enter().append("g").attr("transform", function(d){return "translate("+[d.x,d.y]+")";}).call(drag);
        var circle = groupEnter.append("circle").attr("cx", 0).attr("cy", -4).attr("r", function(d){return d.r;}).attr("class", "Properties");
        var label = circleGroup.append("text").text(function(d){return d.id;}).attr({"alignment-baseline": "middle", "text-anchor": "middle" }).attr("class", "id");    


        force.on('tick', tick);
        force.start();
        var originalPosition = [];
        function dragstart(d) {
            originalPosition[0] = d.x;
            originalPosition[1] = d.y;
            console.log("Start: ", originalPosition[0], originalPosition[1]);
        }
        function drag() {
            var m = d3.mouse(this);
            d3.select(this)
                    .attr('cx', m[0])
                    .attr('cy', m[1]);
        }

        function dragend(d) {
            console.log("End: ", d.x, d.y);
            d3.select(this).transition().attr('cx', originalPosition[0]).attr('cy', originalPosition[1]);
        }

        function tick() {

            circleGroup.attr('transform', function(d) {
                return 'translate(' + d.x + ',' + d.y + ')';
            });

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

        }

</script>

I did something like this on drag start keep a clone of the original graph data like this:我在拖动开始时做了这样的事情,保留原始图形数据的克隆,如下所示:

function dragstart(d) {
    clone1 = JSON.parse(JSON.stringify(graph));
} 

On drag end I copy the stored clones x/y/px/py attributes back to the graph node so that it returns to old position like this:在拖动结束时,我将存储的克隆 x/y/px/py 属性复制回图形节点,使其返回到旧位置,如下所示:

function dragend(d) {
    clone1.nodes.forEach(function(n,i){
        graph.nodes[i].px=n.px
        graph.nodes[i].py=n.py
        graph.nodes[i].x=n.x
        graph.nodes[i].y=n.y
    });
 }

Working code here .工作代码在这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM