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

时间:2014-03-20 17:11:00

标签: javascript json graph d3.js

首先抱歉我的问题,因为我认为这是因为我不太了解D3库,特别是选择的工作。

这是我想做的事情:

  1. 显示将出版物与发布有关出版物的人联系的图表。
  2. 数据更改时更新图表(在这种情况下,当使用时点击上面标有&#34的段落;点击更新")。
  3. 在这里,我不想完全重绘图表;我希望现有节点和链接保持原样,新节点和链接将飞入场景。这就是为什么我将force布局的实例保留在drawGraph函数之外(作为全局变量)。

    任务#1没有问题。

    问题在于任务#2;我可以让新节点进入场景......但由于某种原因我无法拖动现有节点。我只能拖动新节点(Eduardo)。

    我已经在Chrome中调试了它,看到了这个" var a"有9个元素(点击后)。所以,我想函数" force.layout"应该由D3为所有这9个元素调用。所以,如果我没有正确看待,我应该能够拖动所有9个圆圈。

    但事实并非如此,所以我的代码出了问题。任何人都可以指出我弄错了吗?

    这是代码。之后是JSON(两个独立的jsons,news.json和news3.json)。

    其他问题:

    我不太明白为什么这个块(关键函数)在第二次调用drawGraph函数时执行了17次(8 + 9)(当json更新为news3.json时)?我的期望是9次。

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

    提前致谢!,

    拉嘎

    <!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

    {
        "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

    {
        "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}
        ]
    }
    

1 个答案:

答案 0 :(得分:0)

我修改了分配给force.on('tick', ...);上的tick事件的函数后,我得到了它。小修改:现在我使用groupSelection.selectAll('circle')groupSelection.selectAll('text')而不是groupSelection.select('circle')groupSelection.select('text')

您可以看到有效的演示here

以下是代码:

<!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>