简体   繁体   English

D3,JSON,图形,强制布局,数据更新,未完成重绘,拖动

[英]D3, JSON, graph, force layout, data update, not complete redraw, drag

First sorry for my question, because I think this is because I don't understand very well D3 library, especially the working of Selections. 首先对我的问题感到抱歉,因为我认为这是因为我不太了解D3库,尤其是Selections的工作。

Here's what I want to do: 这是我想做的:

  1. Showing a graph that links publications with people who tweet about the publications. 显示一个图表,将出版物与发推文的人联系起来。
  2. Update the graph when the data change (in this case when use clicks the paragraph above that says "click to update"). 数据更改时更新图形(在这种情况下,当使用单击上面的段落中单击“单击更新”时)。

Here I don't want to do complete redraw of the graph; 在这里,我不想完全重画图形; I want the existing nodes and links stay where they are, and new nodes and links fly in to the scene. 我希望现有的节点和链接保持原样,而新的节点和链接飞入现场。 That's why I keep the instance of force layout outside of the drawGraph function (as global variable). 这就是为什么我将力布局实例保留在drawGraph函数之外(作为全局变量)的原因。

Task #1 done without a problem. 任务1顺利完成。

The problem is with task #2; 问题出在任务2上。 I could make the new node enter the scene..., but for some reason I cannot drag the existing nodes. 我可以让新节点进入场景...,但是由于某种原因,我无法拖动现有节点。 I can only drag the new nodes (Eduardo). 我只能拖动新节点(Eduardo)。

I have debugged it in Chrome, and saw this "var a" has 9 elements (after clicking). 我已经在Chrome中对其进行了调试,并看到此“ var a”具有9个元素(单击后)。 So, I suppose the function "force.layout" is supposed to be invoked by D3 for all those 9 elements. 因此,我假设D3应该为所有这9个元素调用函数“ force.layout”。 So, if I undestand correctly, I should've been able to drag all the 9 circles. 因此,如果我对它的理解不正确,那么我应该可以拖动所有9个圆圈。

But that's not the case, so something is wrong with my code. 但是事实并非如此,所以我的代码出了点问题。 Can anybody please point out where I got it wrong? 有人可以指出我做错了什么吗?

Here is the code. 这是代码。 And after that comes the JSON (two separate jsons, news.json and news3.json). 然后是JSON(两个独立的json,news.json和news3.json)。

Additional question: 附加问题:

I don't quite understand why this block (the key function) was executed 17 times (8 + 9) on the second invocation of drawGraph function (when the json is updated to news3.json)? 我不太明白为什么在第二次调用drawGraph函数时(当json更新为news3.json时)此块(键函数)执行了17次(8 + 9)吗? My expectation is 9 times. 我的期望是9倍。

var lineSelections = svg.selectAll('line')
  .data(dataset.edges, function(d){
    console.log(d);
    return d.source.index + '.' + d.target.index;
  });

Thanks in advance!, 提前致谢!,

Raka 拉嘎

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>D3: Force layout</title>
        <script type="text/javascript" src="../d3/d3.v3.js"></script>
        <style type="text/css">
            #tooltip {
                position: absolute;
                width: 200px;
                height: auto;
                padding: 10px;
                background-color: white;
                -webkit-border-radius: 10px;
                -moz-border-radius: 10px;
                border-radius: 10px;
                -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
                -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
                box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
                pointer-events: none;
            }

            #tooltip.hidden {
                display: none;
            }

            #tooltip p {
                margin: 0;
                font-family: sans-serif;
                font-size: 16px;
                line-height: 20px;
            }
        </style>    
    </head>
    <body>
        <div id="tooltip" class="hidden">
            <p><span id="type"></span></p>
            <p><span id="name"></span></p>
        </div>  
        <p id="refresh">Click on this text to update the chart with new data values (once).</p>
        <script type="text/javascript">

            //Width and height
            var w = 500;
            var h = 300;

            //http://stackoverflow.com/questions/16455194/how-to-store-a-json-object-loaded-from-a-file

            var force = d3.layout.force()
                .size([w, h])
                .linkDistance([20])
                .charge([-50]);         
            var svg = d3.select('body')
                .append('svg')
                .attr('width', w)
                .attr('height', h);         

            drawGraph = function(dataset) {
                force
                    .nodes(dataset.nodes)
                    .links(dataset.edges)
                    .start();

                var lineSelections = svg.selectAll('line')
                    .data(dataset.edges, function(d){
                        console.log(d);
                        return d.source.index + '.' + d.target.index;
                    });

                //Create edges as lines
                var edges = lineSelections
                    .enter()
                    .append('line')
                    .style('stroke', function(d) {                  
                        if (d.target.type === 'publication') {
                            return 'red';
                        } else {
                            return 'blue';
                        }
                    })
                    .style('stroke-width', function(d) {
                        return d.weight;
                    });     

                var groups = svg.selectAll('g')
                    .data(dataset.nodes, function(d) {
                        console.log(d.name);
                        return d.name;
                    })
                    .enter()
                    .append('g');

                var nodes = groups
                    .append('circle')
                    .attr('r', function(d) {
                        if (d.type === 'publication') {
                            var radius = d.weight / 6;
                            if (radius < 5) {
                                radius = 5;
                            } 
                            return radius;
                        } else {
                            return d.weight * 3;
                        }
                    })
                    .style('fill', function(d, i) {
                        if (d.type === 'publication') {
                            return 'black';
                        } else {
                            return 'green';
                        }
                    });

                var a = svg.selectAll('g circle');
                a.call(force.drag);

                //Create labels
                var text = groups
                   .append('text')
                   .text(function(d) {
                        if (d.type === 'publication') {
                            return d.name;
                        } else {
                            return '';
                        }
                   })
                   .attr('x', function(d, i) {
                        d.x;
                   })
                   .attr('y', function(d) {
                        d.x;
                   })
                   .attr('font-family', 'sans-serif')
                   .attr('font-size', '24px')
                   .attr('fill', 'orange');             

                groups
                    .on("mouseover", function(d) {
                        //Get this bar's x/y values, then augment for the tooltip
                        //var hmm = d3.select(this).select('circle').attr('cx');
                        var xPosition = d3.select(this).select('circle').attr('cx');
                        var yPosition = d3.select(this).select('circle').attr('cy');

                        //Update the tooltip position and value
                        d3.select("#tooltip")
                            .style("left", xPosition + "px")
                            .style("top", yPosition + "px")
                            .select("#type")
                            .text(d.type);

                        d3.select("#tooltip")
                            .style("left", xPosition + "px")
                            .style("top", yPosition + "px")
                            .select("#name")
                            .text(d.name);                          

                        //Show the tooltip
                        //d3.select("#tooltip").classed("hidden", false);
                    })
                    .on("mouseout", function() {
                        //Hide the tooltip
                        d3.select("#tooltip").classed("hidden", true);
                    })


                //Every time the simulation "ticks", this will be called
                force.on('tick', function() {
                    edges.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; });

                    nodes.attr('cx', function(d) { return d.x; })
                         .attr('cy', function(d) { return d.y; });


                    //Update all labels
                    svg.selectAll('text')
                       .data(dataset.nodes)
                       .attr('x', function(d, i) {
                            return d.x;
                       })
                       .attr('y', function(d) {
                            return d.y;
                       });                   

                });
            }

            d3.json('news.json', function(dataset) {
                drawGraph(dataset);
            });

            d3.select("p")
                .on("click", function() {
            });

            d3.select("#refresh")
                .on("click", function() {
                d3.json('news3.json', function(dataset) {
                    console.log(dataset);
                    drawGraph(dataset);
                });
            });
        </script>
    </body>
</html>

news.json news.json

{
    "nodes": [
        { "name": "El Universal", "type": "publication", "weight": 10 },
        { "name": "Milenio", "type": "publication", "weight": 4},
        { "name": "Proceso", "type": "publication", "weight": 4},
        { "name": "Paco", "type": "person", "weight": 12},
        { "name": "Juan", "type": "person", "weight": 5},
        { "name": "Alberto", "type": "person", "weight": 5 },
        { "name": "Xochitl", "type": "person", "weight": 3 },
        { "name": "Reforma", "type": "publication", "weight": 2}
    ],
    "edges": [
        { "source": 3, "target": 0, "weight": 9},
        { "source": 3, "target": 1, "weight": 3},
        { "source": 4, "target": 2, "weight": 4},
        { "source": 4, "target": 0, "weight": 1},
        { "source": 5, "target": 3, "weight": 5},
        { "source": 6, "target": 1, "weight": 1},
        { "source": 6, "target": 7, "weight": 2},
        { "source": 6, "target": 1, "weight": 1},
        { "source": 3, "target": 5, "weight": 4},
        { "source": 4, "target": 5, "weight": 1}
    ]
}

news3.json news3.json

{
    "nodes": [
        { "name": "El Universal", "type": "publication", "weight": 10 },
        { "name": "Milenio", "type": "publication", "weight": 4},
        { "name": "Proceso", "type": "publication", "weight": 4},
        { "name": "Paco", "type": "person", "weight": 12},
        { "name": "Juan", "type": "person", "weight": 5},
        { "name": "Alberto", "type": "person", "weight": 5 },
        { "name": "Xochitl", "type": "person", "weight": 3 },
        { "name": "Reforma", "type": "publication", "weight": 2},
        { "name": "Eduardo", "type": "person", "weight": 2}
    ],
    "edges": [
        { "source": 3, "target": 0, "weight": 9},
        { "source": 3, "target": 1, "weight": 3},
        { "source": 4, "target": 2, "weight": 4},
        { "source": 4, "target": 0, "weight": 1},
        { "source": 5, "target": 3, "weight": 5},
        { "source": 6, "target": 1, "weight": 1},
        { "source": 6, "target": 7, "weight": 2},
        { "source": 6, "target": 1, "weight": 1},
        { "source": 3, "target": 5, "weight": 4},
        { "source": 4, "target": 5, "weight": 1},
        { "source": 8, "target": 7, "weight": 2}
    ]
}

I got it working, after I modified the function assigned to the tick event on force.on('tick', ...); 在修改对force.on('tick', ...);的tick事件分配的函数后,它开始工作force.on('tick', ...); . Small modif: instead of using groupSelection.selectAll('circle') and groupSelection.selectAll('text') , now I use groupSelection.select('circle') and groupSelection.select('text') . 小型修改:现在,我不再使用groupSelection.selectAll('circle')groupSelection.selectAll('text') ,而是使用groupSelection.select('circle')groupSelection.select('text')

You can see a working demo here . 您可以在此处查看有效的演示。

Here is the code: 这是代码:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>D3: Force layout</title>
        <script type="text/javascript" src="../d3/d3.v3.js"></script>
        <style type="text/css">
            #tooltip {
                position: absolute;
                width: 200px;
                height: auto;
                padding: 10px;
                background-color: white;
                -webkit-border-radius: 10px;
                -moz-border-radius: 10px;
                border-radius: 10px;
                -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
                -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
                box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
                pointer-events: none;
            }

            #tooltip.hidden {
                display: none;
            }

            #tooltip p {
                margin: 0;
                font-family: sans-serif;
                font-size: 16px;
                line-height: 20px;
            }
        </style>    
    </head>
    <body>
        <div id="tooltip" class="hidden">
            <p><span id="type"></span></p>
            <p><span id="name"></span></p>
        </div>  
        <p id="refresh">Click on this text to update the chart with new data values (once).</p>
        <script type="text/javascript">

            //Width and height
            var w = 300;
            var h = 200;

            //http://stackoverflow.com/questions/16455194/how-to-store-a-json-object-loaded-from-a-file

            var force = d3.layout.force()
                .size([w, h])
                .linkDistance([20])
                .charge([-50]);         
            var svg = d3.select('body')
                .append('svg')
                .attr('width', w)
                .attr('height', h);         

            drawGraph = function(dataset) {
                force.nodes(dataset.nodes);
                force.links(dataset.edges);
                force.start();

                var lineSelections = svg.selectAll('line')
                    .data(dataset.edges, function(d){
                        return '.'.concat(d.source.name, '.', d.target.name);
                    });

                lineSelections
                    .enter()
                    .append('line')
                    .style('stroke', function(d) {                  
                        if (d.target.type === 'publication') {
                            return 'red';
                        } else {
                            return 'blue';
                        }
                    })
                    .style('stroke-width', function(d) {
                        return d.weight;
                    });     

                var groupSelection = svg.selectAll('g')
                    .data(dataset.nodes, function(d) {
                        return d.name;
                    })
                    .call(force.drag);

                var groups = groupSelection
                    .enter()
                    .append('g')
                    .call(force.drag);

                groups
                    .append('circle')
                    .attr('r', function(d) {
                        if (d.type === 'publication') {
                            var radius = d.weight / 6;
                            if (radius < 5) {
                                radius = 5;
                            } 
                            return radius;
                        } else {
                            return d.weight * 3;
                        }
                    })
                    .style('fill', function(d, i) {
                        if (d.type === 'publication') {
                            return 'black';
                        } else {
                            return 'green';
                        }
                    });

                //Create labels
                groups
                   .append('text')
                   .text(function(d) {
                        if (d.type === 'publication') {
                            return d.name;
                        } else {
                            return d.name;
                        }
                   })
                   .attr('x', function(d, i) {
                        d.x;
                   })
                   .attr('y', function(d) {
                        d.x;
                   })
                   .attr('font-family', 'sans-serif')
                   .attr('font-size', '14px')
                   .attr('fill', 'orange');         

                //Every time the simulation "ticks", this will be called
                force.on('tick', function() {           
                    lineSelections
                        .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; 
                        });

                    groupSelection
                        .select('circle')
                        .attr('cx', function(d) { 
                            return d.x; 
                        })
                        .attr('cy', function(d) {
                            return d.y; 
                        });

                    //Update all labels
                    groupSelection
                        .select('text')
                        .attr('x', function(d, i) {
                            return d.x;
                        })
                        .attr('y', function(d) {
                            return d.y;
                        });
                });
            }

            d3.json('news.json', function(dataset) {
                drawGraph(dataset);
            });

            d3.select("p")
                .on("click", function() {
            });

            d3.select("#refresh")
                .on("click", function() {
                console.log('==================');
                d3.json('news3.json', function(dataset) {
                    drawGraph(dataset);
                });
            });
        </script>
    </body>
</html>

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

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