简体   繁体   English

强制定向图和本地存储

[英]Forced-Directed Graph and localstorage

I'm trying to store nodes positions after a drag and drop in localStorage, but when I reload the page, the links are where I saved them, nodes aswell but links and nodes are not linked so the nodes just go away from their initial positions. 我试图在拖放到localStorage之后存储节点位置,但是当我重新加载页面时,链接是我保存它们的位置,节点也是如此,但是链接和节点没有链接,因此节点只是偏离了它们的初始位置。

Here my code, i'm using angular. 在这里,我的代码,我正在使用角度。

angular.module('core').controller('HomeController', ['$scope', 
function($scope) {
    $scope.graph = {
        width : 500,
        height : 400,
        color : d3.scale.category20(),
        force : '',
        drag : '',
        dragstart : function(d) {
            d.x = d3.event.x;
            d.y = d3.event.y;
        },
        dragend : function(d) {
            var graphTmp = { "nodes" : $scope.graph.node.data(), "links" : $scope.graph.link.data()};
            localStorage.setItem('graph',JSON.stringify(graphTmp));
        },
        link : [],
        node : [],
        links : [],
        nodes : []
    };
    $scope.svg = d3.select("body").append("svg")
            .attr("width", $scope.graph.width)
            .attr("height", $scope.graph.height);
    $scope.savedGraph = {};

    $scope.draw = function(){
        $scope.graph.force = d3.layout.force()
                .charge(-120)
                .linkDistance(30)
                .size([$scope.graph.width, $scope.graph.height]);

        $scope.graph.force
              .nodes($scope.graph.nodes)
              .links($scope.graph.links)
              .start();

        $scope.graph.link = $scope.svg.selectAll(".link")
            .data($scope.graph.links)
            .enter().append("line")
            .attr("class", "link")
            .style({'stroke' : 'gray', 'stroke-width' : '1px'});

        $scope.graph.drag = $scope.graph.force.drag()
            .on("dragstart", $scope.graph.dragstart)
            .on("dragend", $scope.graph.dragend);

        $scope.graph.node = $scope.svg.selectAll(".node")
            .data($scope.graph.nodes)
            .enter().append("circle")
            .attr("class", "node")
            .attr("r", 5)
            .style("fill", function(d) { return $scope.graph.color(d.group); })
            .call($scope.graph.drag);

        $scope.graph.node
            .append("title")
            .text(function(d) { return d.name; });

        $scope.graph.force.on("tick", function() {
            $scope.graph.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; });

            $scope.graph.node
                .attr("cx", function(d) { return d.x; })
                .attr("cy", function(d) { return d.y; });
        });
    };

    if(localStorage.getItem('graph') === null){

        $scope.graph.nodes = [
            {"name":"Myriel","group":1},
            {"name":"Napoleon","group":1},
            {"name":"Mlle.Baptistine","group":1},
            {"name":"Mme.Magloire","group":1},
            {"name":"CountessdeLo","group":1},
            {"name":"Geborand","group":1},
            {"name":"Champtercier","group":1},
            {"name":"Cravatte","group":1},
            {"name":"Count","group":1}
        ];

        $scope.graph.links = [
            {"source":1,"target":0,"value":1},
            {"source":2,"target":0,"value":8},
            {"source":3,"target":0,"value":10},
            {"source":3,"target":2,"value":6},
            {"source":4,"target":0,"value":1}
        ];

        $scope.draw();
    }
    else {
        var graphTmp = $.parseJSON(localStorage.getItem('graph'));
        $scope.graph.links = graphTmp.links;
        $scope.graph.nodes = graphTmp.nodes;
        $scope.draw();

    }

Does someone know why ? 有人知道为什么吗? I think it's because nodes and links are not able to be linked together with only node.data() and link.data() datas. 我认为这是因为节点和链接无法仅与node.data()和link.data()数据链接在一起。 May I store more datas ? 我可以存储更多数据吗?

Thanks ! 谢谢 !

The problem is that initially when you load the links(there is no data in localstorage) it has this structure: 问题在于,最初加载链接时(本地存储中没有数据),它具有以下结构:

 $scope.graph.links = [{
            "source": 1,
            "target": 0,
            "value": 1
        }, {
            "source": 2,
            "target": 0,
            "value": 8
        }, {
            "source": 3,
            "target": 0,
            "value": 10
        }, {
            "source": 3,
            "target": 2,
            "value": 6
        }, {
            "source": 4,
            "target": 0,
            "value": 1
        }];

Now the force layout will replace the source index with the source object from nodes. 现在,强制布局将用节点中的源对象替换源索引。

When you drag you save the nodes and the links(the links object will hold node object for the source and target). 拖动时,保存节点和链接(链接对象将保存源和目标的节点对象)。

Now when you load from the local storage you parse the links, the links source/target will be having a different object from the nodes object(because its deep cloned). 现在,当您从本地存储加载时,您将分析链接,链接源/目标将具有与节点对象不同的对象(因为其深度克隆)。

Thus drag changes on node will not be reflected on the links source/target object.Hence the links go detached. 因此,节点上的拖动更改将不会反映在链接源/目标对象上。因此,链接将断开。

Solution for the problem is that you always store the indexe form of links in localstorage. 该问题的解决方案是,您始终将链接的索引形式存储在localstorage中。 I am doing this here by keeping another object initLinks 我在这里通过保持另一个对象initLinks来执行此操作

$scope.graph.nodes = [{
    "name": "Myriel",
    "group": 1
}, {
    "name": "Napoleon",
    "group": 1
}, {
    "name": "Mlle.Baptistine",
    "group": 1
}, {
    "name": "Mme.Magloire",
    "group": 1
}, {
    "name": "CountessdeLo",
    "group": 1
}, {
    "name": "Geborand",
    "group": 1
}, {
    "name": "Champtercier",
    "group": 1
}, {
    "name": "Cravatte",
    "group": 1
}, {
    "name": "Count",
    "group": 1
}];

$scope.graph.links = [{
    "source": 1,
    "target": 0,
    "value": 1
}, {
    "source": 2,
    "target": 0,
    "value": 8
}, {
    "source": 3,
    "target": 0,
    "value": 10
}, {
    "source": 3,
    "target": 2,
    "value": 6
}, {
    "source": 4,
    "target": 0,
    "value": 1
}];
$scope.graph.initLinks = angular.copy($scope.graph.links);

On saving to localstorage i do this 在保存到本地存储时,我这样做

    var graphTmp = {
        "nodes": $scope.graph.nodes,
        "links": $scope.graph.initLinks
    };

    localStorage.setItem('graph', JSON.stringify(graphTmp));

While loading from localstorage i do this 从本地存储加载时,我这样做

var graphTmp = JSON.parse(localStorage.getItem('graph'));
$scope.graph.links =  graphTmp.links;
$scope.graph.initLinks = angular.copy(graphTmp.links);
$scope.graph.nodes = graphTmp.nodes;
$scope.draw();

Working code here 这里的工作代码

Hope this helps! 希望这可以帮助!

to expand on cyrils answer, if you have complex data in links. 如果链接中包含复杂数据,则可扩展cyrils答案。 you might want to substitute the object reference itself with its index. 您可能想要用其索引替换对象引用本身。 all other data is preserved. 所有其他数据都将保留。 Note that in my application I save link data separately in localstorage . 请注意,在我的应用程序中,我将链接数据分别保存在localstorage Nodes are saved with another key. 节点用另一个密钥保存。

store(key: string, data: any) {
if (key === 'links') {
  // replace all targets and source with indexes, we do not need to serialize the values.
  localStorage.setItem(key, JSON.stringify(data, (k, v) => {
    if (k === 'source' || k === 'target') {
      return v.index;
    } else {
      return v;
    }

  }));

} else {
  localStorage.setItem(key, JSON.stringify(data));
}
}

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

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