javascript - D3、JSON、图表、强制布局、数据更新、不完全重绘、拖动

标签 javascript json graph d3.js

首先对我的问题表示抱歉,因为我认为这是因为我不太了解 D3 库,尤其是 Selections 的工作原理。

这就是我想做的:

  1. 显示一个图表,将出版物与发布有关该出版物的推文的人联系起来。
  2. 当数据发生变化时更新图表(在这种情况下,当用户点击上面的“点击更新”段落时)。

这里我不想完全重绘图表;我希望现有的节点和链接保持在原来的位置,而新的节点和链接飞入场景。这就是为什么我将力布局的实例保留在drawGraph函数之外(作为全局变量)。

任务 #1 顺利完成。

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

我在Chrome中调试了它,看到这个“var a”有9个元素(点击后)。因此,我认为 D3 应该为所有这 9 个元素调用函数“force.layout”。所以,如果我理解正确的话,我应该能够拖动所有 9 个圆圈。

但事实并非如此,所以我的代码有问题。谁能指出我哪里错了?

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

附加问题:

我不太明白为什么这个 block (关键函数)在第二次调用drawGraph函数时(当json更新为news3.json时)执行了17次(8 + 9)?我的期望是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}
    ]
}

最佳答案

在我修改了分配给 force.on('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>

关于javascript - D3、JSON、图表、强制布局、数据更新、不完全重绘、拖动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22540348/

相关文章:

matplotlib - dpi 与图形大小的关系

javascript - Wordpress css 类干扰 slider

javascript - 基本 JSON.parse 问题

javascript - 如何为 JQuery $.ajax 同步请求设置超时并执行某些操作

json - Shopify 履行

javascript - 在 javascript 中使用 map 更改数组

algorithm - 二分图的快速最大匹配算法

java - 如何将我的数据加载到 GraphML 文件中以便于在 Prefuse 中使用?

javascript - 抓取 CSV 格式的网站数据

javascript - 需要一个正则表达式来验证长度为 6 到 25 个字符的字母数字