d3js强制布局在展开后隐藏/取消隐藏节点单击错放节点 [英] d3js force layout with hide/unhide on node click misplaces nodes after expanding

查看:2314
本文介绍了d3js强制布局在展开后隐藏/取消隐藏节点单击错放节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图使用d3和力布局创建一个图形。我已使用以下示例来了解如何添加它们。



我的图表也会变大对用户与节点的交互,如果用户点击一个没有孩子的节点,ajax请求将去后端服务以请求更多的节点。该图表意在用作关系发现应用程序。我创建了以下jsfiddle http://jsfiddle.net/R2JGa/7/ 来获得一个想法的我想要实现的。



它工作相对较好,但我有一个令人讨厌的问题:当添加新节点到图中旧节点不知何故被放错地方。例如,我从3个节点开始,根节点是flare。其他2个节点是动画和分析。在我的例子中,当你点击当前没有子节点的节点时,子节点将总是x,y,z,t扩展几个节点后,您将看到动画或分析未链接到根节点flare,而是链接到一些其他节点(x,y,z,t)。或者有时如果展开x或y或z或t,则子节点具有重复的x或y或z或t。如果单击flare隐藏整个图形,然后重新打开flare,您将看到节点已正确链接和命名。



我似乎无法看到为什么会发生这种情况。有人能在这里散发一些光吗?我仍然是新的d3,发现它真的很有趣,但这些问题是如此烦人...



这里是代码:

  var w = 960,
h = 800,
node,
link,
root;

var force = d3.layout.force()
.charge(-1000)
.size([w,h]);

var vis = d3.select(#chart)。append(svg:svg)
.attr(width,w)
.attr高度,h);

d3.json(data.json,function(json){
root = json;
update();
});

function update(){
var nodes = flatten(root);
nodes.reverse();
nodes = nodes.sort(function(a,b){
return a.index - b.index;
});

var links = d3.layout.tree()。​​links(nodes);

console.log(nodes);

//重新启动强制布局。
force
.nodes(nodes)
.links(links)
.linkDistance(55)
.start();


var link = vis.selectAll(。link)
.data(links);

link.enter()。append(line)
.attr(class,link);

link.exit()。remove();

var node = vis.selectAll(g.node)
.data(nodes)


var groups = node.enter .append(g)
.attr(class,node)
.attr(id,function(d){
return d.id
})
.on('click',click)
.call(force.drag);


groups.append(image)
.attr(xlink:href,https://github.com/favicon.ico)
.attr(x,-8)
.attr(y,-8)
.attr(width,16)
.attr );


groups.append(text)
.attr(dx,12)
.attr(dy,0.35em)
.style(font-size,10px)
.text(function(d){
console.log(d);
return d.name
});


node.exit()。remove();


force.on(tick,function(){
link.attr(x1,function(d){
return d.source。 x;
})
.attr(y1,function(d){
return d.source.y;
})
.attr ,function(d){
return d.target.x;
})
.attr(y2,function(d){
return d.target.y ;
});

node.attr(transform,function(d){
returntranslate(+ dx +,+ dy +) ;
});
});
}


//颜色叶节点为橙色,打包为白色或蓝色。
function color(d){
return d._children? #3182bd:d.children? #c6dbef:#fd8d3c;
}

//点击切换子项。
function click(d){
console.log(d);
if(d.children){
d._children = d.children;
d.children = null;
update();
} else if(d._children){
d.children = d._children;
d._children = null;
update();
}
else {
d3.json(expand.json,function(json){
d.children = json.children;
update
})
}
}

//返回根下所有节点的列表。
function flatten(root){
var nodes = [],i = 0;

function recurse(node){
if(node.children)node.children.forEach(recurse);
if(!node.id)node.id = ++ i;
nodes.push(node);
}

recurse(root);
返回节点;
}

这里是我要求的2个json文件:



data.json

  {
name:flare ,
id:flare,
children:[
{
name:analytics

},
{
name:animate,
id:animate
}
] $ b}

和expand.json

  {children:[
{
name:x,
id:x,
size:1983
},
{
name:y,
id:y,
size:2047
},
{
name:z,
id:z,
size:1375
},
{
name:t,
id:t,
size:1375
}
]}



PS:我不得不对节点数组进行排序,否则图形中发生了错误,我不明白为什么。

解决方案

这里 fiddle 到工作解决方案。我认为问题是与你声明你的id和排序他们基于数组索引的方式。你应该让id被展开的代码声明,然后根据id的给定排序。在你的递归函数中,你可能先声明父类,然后声明子类。

 函数recurse $ b if(!node.id)node.id = ++ i; 
nodes.push(node);
if(node.children)node.children.forEach(recurse);
}


I am trying to create a graph using d3 and force layout. I have used the following example http://bl.ocks.org/mbostock/1062288 to get started:

I also need images and labels so I looked at this example http://bl.ocks.org/mbostock/950642 to get an idea how I could add them.

My graph will also get bigger depending on the user interactions with the nodes so if a user clicks on a node that doesn't have children, an ajax request will go to the backend service to request more nodes. The graph is meant to be used as a relationship discovery application. I created the following jsfiddle http://jsfiddle.net/R2JGa/7/ to get an idea of what i'm trying to achieve.

It works relatively well but I have one annoying problem: when adding new nodes to the graph the old nodes somehow get misplaced. For instance, I start with 3 nodes, root node is "flare". The other 2 nodes are "animate" and "analytics". In my example the children will always be "x","y","z","t" anytime you click on a node that currently doesn't have children. After expanding a few nodes you will see that "animate" or "analytics" are not linked to the root node "flare" but to some other nodes (x,y,z,t). Or sometimes if you expand x or y or z or t the children nodes have a duplicate x or y or z or t. If you click on "flare" to hide the entire graph and then reopen "flare" you will see that the nodes are correctly linked and named.

I can't seem to see why this is happening. Could somebody shed some light here? I am still new to d3 and find it really interesting but these problems are so annoying...

Here is the code:

var w = 960,
            h = 800,
            node,
            link,
            root;

    var force = d3.layout.force()
            .charge(-1000)
            .size([w, h]);

    var vis = d3.select("#chart").append("svg:svg")
            .attr("width", w)
            .attr("height", h);

    d3.json("data.json", function (json) {
        root = json;
        update();
    });

    function update() {
        var nodes = flatten(root);
        nodes.reverse();
        nodes = nodes.sort(function (a, b) {
            return a.index - b.index;
        });

        var links = d3.layout.tree().links(nodes);

        console.log(nodes);

        // Restart the force layout.
        force
                .nodes(nodes)
                .links(links)
                .linkDistance(55)
                .start();


        var link = vis.selectAll(".link")
                .data(links);

        link.enter().append("line")
                .attr("class", "link");

        link.exit().remove();

        var node = vis.selectAll("g.node")
                .data(nodes)


        var groups = node.enter().append("g")
                .attr("class", "node")
                .attr("id", function (d) {
                    return d.id
                })
                .on('click', click)
                .call(force.drag);


        groups.append("image")
                .attr("xlink:href", "https://github.com/favicon.ico")
                .attr("x", -8)
                .attr("y", -8)
                .attr("width", 16)
                .attr("height", 16);


        groups.append("text")
                .attr("dx", 12)
                .attr("dy", "0.35em")
                .style("font-size", "10px")
                .text(function (d) {
                    console.log(d);
                    return d.name
                });


        node.exit().remove();


        force.on("tick", function () {
            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("transform", function (d) {
                return "translate(" + d.x + "," + d.y + ")";
            });
        });
    }


    // Color leaf nodes orange, and packages white or blue.
    function color(d) {
        return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c";
    }

    // Toggle children on click.
    function click(d) {
        console.log(d);
        if (d.children) {
            d._children = d.children;
            d.children = null;
            update();
        } else if (d._children) {
            d.children = d._children;
            d._children = null;
            update();
        }
        else {
            d3.json("expand.json", function (json) {
                d.children = json.children;
                update();
            })
        }
    }

    // Returns a list of all nodes under the root.
    function flatten(root) {
        var nodes = [], i = 0;

        function recurse(node) {
            if (node.children) node.children.forEach(recurse);
            if (!node.id) node.id = ++i;
            nodes.push(node);
        }

        recurse(root);
        return nodes;
    }

And here are the 2 json files I'm requesting:

data.json

{
    "name": "flare",
    "id" : "flare",
    "children": [
        {
            "name": "analytics",
            "id": "analytics"

        },
        {
            "name": "animate",
            "id": "animate"
        }
    ]
}

And expand.json

{"children": [
    {
        "name": "x",
        "id": "x",
        "size": 1983
    },
    {
        "name": "y",
        "id": "y",
        "size": 2047
    },
    {
        "name": "z",
        "id": "z",
        "size": 1375
    },
    {
        "name": "t",
        "id": "t",
        "size": 1375
    }
]}

PS: i had to sort the nodes array otherwise bad things happened to the graph, i cannot understand why.

解决方案

Here the fiddle to the working solution. I think the problem was with the way you are declaring your id's and sorting them based on array index. You should have let the id's being declared by the flattening code and then sort them based on id's given. Also in your recursive function you might want to declare the parent first and then then children.

function recurse(node) {
        if(!node.id) node.id = ++i;
        nodes.push(node);
        if (node.children) node.children.forEach(recurse);
    }

这篇关于d3js强制布局在展开后隐藏/取消隐藏节点单击错放节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆