使用流数据更新嵌套的 d3 气泡图
Updating nested d3 bubble chart with streaming data
我一直在尝试为我的一个项目实现嵌套气泡图,在该项目中我必须显示网络中不同客户端发生的情况。我使用了显示在以下位置的示例:
https://bl.ocks.org/mbostock/7607535
这是我使用的代码:
/// <reference path="../bower_components/dt-d3/d3.d.ts" />
var margin = 20, diameter = 960;
var color = d3.scale.linear()
.domain([-1, 4])
.range(["#ff8080", "#b30000"])
.interpolate(d3.interpolateHcl);
var pack = d3.layout.pack()
.padding(2)
.size([diameter - margin, diameter - margin])
.value(function (d) { return d.size; });
var svg = d3.select("body").select("#parent").select("#svg1").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
var svg2 = d3.select("body").select("#parent").select("#svg2").append("svg")
.attr("width", 100)
.attr("height", 100)
.append("g")
.attr("transform", "translate(" + margin + "," + margin + ")");
function draw(fileToRead){
console.log(fileToRead);
d3.json(fileToRead, function (error, root) {
if (error)
throw error;
var focus = root, nodes = pack.nodes(root), view;
var circle = svg.selectAll("circle").data(nodes);
circle.attr("class","update");
circle.enter().append("circle")
.attr("class", function (d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; }) .style("fill", function (d) { return d.children ? d.children.length > 3 ? color(d.depth) : "white" : d.size > 0.02 ? "#660000" : "#003366"; })
.style("fill", function (d) { return d.children ? "white" : d.size > 0.02 ? "#660000" : "#003366"; })
.on("click", function (d) { if (focus !== d)
zoom(d), d3.event.stopPropagation(); });
circle.exit().remove();
var text = svg.selectAll("text").data(nodes);
text.attr("class","update");
text.enter().append("text")
.attr("class", "label")
.style("fill-opacity", function (d) { return d.parent === root ? 1 : 0; })
.style("display", function (d) { return d.parent === root ? "inline" : "none"; })
.style("fill", "black")
.text(function (d) { return d.name; });
text.exit().remove();
var node = svg.selectAll("circle,text");
d3.select("body")
.style("background", color(-1))
.on("click", function () { zoom(root); });
zoomTo([root.x, root.y, root.r * 2 + margin]);
function zoom(d) {
var focus0 = focus;
focus = d;
if (focus.depth == 2) {
circle2.style("fill", "white");
} else {
circle2.style("fill", "black");
}
var transition = d3.transition()
.duration(d3.event.altKey ? 7500 : 750)
.tween("zoom", function (d) {
var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
return function (t) { zoomTo(i(t)); };
});
transition.selectAll("text")
.filter(function (d) { return d.parent === focus || this.style.display === "inline"; })
.style("fill-opacity", function (d) { return d.parent === focus ? 1 : 0; })
.each("start", function (d) { if (d.parent === focus)
this.style.display = "inline"; })
.each("end", function (d) { if (d.parent !== focus)
this.style.display = "none"; });
}
function zoomTo(v) {
var k = diameter / v[2];
view = v;
node.attr("transform", function (d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
circle.attr("r", function (d) { return d.r * k; });
}
});
}
d3.select(self.frameElement).style("height", diameter + "px");
我遇到的问题是当我不断地将新数据流式传输到 UI 时。每次我用一个完整的 json 文件调用 "Draw",其中包含我在数据库中的所有数据。我读过 enter()、exit()、remove() 负责更新 DOM 元素,但似乎我遗漏了一些东西。当调用 draw() 函数时,它似乎在现有气泡上方绘制气泡,并且圆形边框颜色或文本边框颜色等内容消失。
我想知道我是否以正确的方式使用 enter() 和 exit() 命令。我想让 draw() 函数接收 json 文件,并且只更新气泡图中的新信息并且不存在 DOM 元素,但它似乎只是重新创建所有 DOM 个元素。
提前谢谢你,
乔治
PS:我有两个 svg,因为我试图将屏幕一分为二,左侧被气泡图占据,右侧显示额外信息,当我们将它放大到内部时气泡
EDIT1:这是我传递给 draw() 函数的数据示例:
{"name": "",
"children":
[{"name": "10.0.0.64", "children":
[{"name": "unidentified", "children":
[{"name": "datasource", "size": 0.002551020408163265}, {"name": "datasource", "size": 0.0031746031746031746}, {"name": "datasource", "size": 0.0017123779389460522}, {"name": "average", "size": 0.008550406895439414}, {"name": "datasource", "size": 0.019020266320109332}]},
{"name": "www.quefaire.be", "children":
[{"name": "average", "size": 0.002270490544565449}, {"name": "datasource", "size": 0.023243328100470965}]}, {"name": "tnsinternet.be", "children": [{"name": "average", "size": 0.002199967070440535}, {"name": "datasource", "size": 0.022009167303284966}]},
{"name": "hy.sachinese.com", "children":
[{"name": "average", "size": 0.0022121416372369493}, {"name": "datasource", "size": 0.022222222222222223}]}, {"name": "pagesdor.be", "children": [{"name": "average", "size": 0.0030411250824668935}, {"name": "datasource", "size": 0.03672943251374624}]},
{"name": "r.254a.com", "children":
[{"name": "average", "size": 0.0018942905665264935}, {"name": "datasource", "size": 0.015873015873015872}]},
{"name": "i.ctnsnet.com", "children":
[{"name": "average", "size": 0.0018942905665264935}, {"name": "datasource", "size": 0.015873015873015872}]},
{"name": "link.carrefour.eu", "children":
[{"name": "average", "size": 0.0019793245801319357}, {"name": "datasource", "size": 0.017857142857142856}]},
{"name": "goudengids.be", "children":
[{"name": "average", "size": 0.00457041828122788}, {"name": "datasource", "size": 0.06349206349206349}]}, {"name": "www.inmemoriam.be", "children": [{"name": "average", "size": 0.0018443560093702978}, {"name": "datasource", "size": 0.014707876206037973}]},
{"name": "www1.gfk-wi.com", "children":
[{"name": "datasource", "size": 0.011986645572622365}, {"name": "average", "size": 0.0017277318393667718}]}, {"name": "bootstrapcdn.com", "children": [{"name": "average", "size": 0.008550406895439414}, {"name": "datasource", "size": 0.13314186424076532}]},
{"name": "maxcdn.bootstrapcdn.com", "children":
[{"name": "datasource", "size": 0.031746031746031744}, {"name": "average", "size": 0.0027563593243117796}]}]}]}
这也是我的 CSS:
.svg-container {
display: flex;
position: relative;
width: 100%;
padding-bottom: 100%; /* aspect ratio */
vertical-align: top;
overflow: hidden;
}
.svg-content-responsive {
display: inline-block;
position: absolute;
top: 10px;
left: 0;
}
.container {
float: left;
}
.node {
cursor: pointer;
stroke: darkgrey;
}
.node:hover {
stroke: #000;
stroke-width: 1.5px;
}
.node--leaf {
fill: white;
}
.label {
font: 11px "Helvetica Neue", Helvetica, Arial, sans-serif;
text-anchor: middle;
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff, 0 -1px 0 #fff;
}
.label,
.node--root,
.node--leaf {
pointer-events: none;
}
首先确保所有的节点都有唯一的名字或者必须有一些id来唯一定义一个节点。
使用如下所示的唯一键来唯一标识数据。
在您的数据集中,我的解决方案将不起作用,原因是很少有节点具有非唯一名称,例如数据集等。
以下解决方案只有在名称唯一时才有效。
而不是
var circle = svg.selectAll("circle").data(nodes)
;
这样做
var circle = svg.selectAll("circle").data(nodes, function(d){return d.name});
而不是这个
var text = svg.selectAll("text").data(nodes);
这样做
var text = svg.selectAll("text").data(nodes, function(d){return d.name});
exit()
功能因上述原因无法正常工作
了解关键功能 here
我一直在尝试为我的一个项目实现嵌套气泡图,在该项目中我必须显示网络中不同客户端发生的情况。我使用了显示在以下位置的示例: https://bl.ocks.org/mbostock/7607535
这是我使用的代码:
/// <reference path="../bower_components/dt-d3/d3.d.ts" />
var margin = 20, diameter = 960;
var color = d3.scale.linear()
.domain([-1, 4])
.range(["#ff8080", "#b30000"])
.interpolate(d3.interpolateHcl);
var pack = d3.layout.pack()
.padding(2)
.size([diameter - margin, diameter - margin])
.value(function (d) { return d.size; });
var svg = d3.select("body").select("#parent").select("#svg1").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
var svg2 = d3.select("body").select("#parent").select("#svg2").append("svg")
.attr("width", 100)
.attr("height", 100)
.append("g")
.attr("transform", "translate(" + margin + "," + margin + ")");
function draw(fileToRead){
console.log(fileToRead);
d3.json(fileToRead, function (error, root) {
if (error)
throw error;
var focus = root, nodes = pack.nodes(root), view;
var circle = svg.selectAll("circle").data(nodes);
circle.attr("class","update");
circle.enter().append("circle")
.attr("class", function (d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; }) .style("fill", function (d) { return d.children ? d.children.length > 3 ? color(d.depth) : "white" : d.size > 0.02 ? "#660000" : "#003366"; })
.style("fill", function (d) { return d.children ? "white" : d.size > 0.02 ? "#660000" : "#003366"; })
.on("click", function (d) { if (focus !== d)
zoom(d), d3.event.stopPropagation(); });
circle.exit().remove();
var text = svg.selectAll("text").data(nodes);
text.attr("class","update");
text.enter().append("text")
.attr("class", "label")
.style("fill-opacity", function (d) { return d.parent === root ? 1 : 0; })
.style("display", function (d) { return d.parent === root ? "inline" : "none"; })
.style("fill", "black")
.text(function (d) { return d.name; });
text.exit().remove();
var node = svg.selectAll("circle,text");
d3.select("body")
.style("background", color(-1))
.on("click", function () { zoom(root); });
zoomTo([root.x, root.y, root.r * 2 + margin]);
function zoom(d) {
var focus0 = focus;
focus = d;
if (focus.depth == 2) {
circle2.style("fill", "white");
} else {
circle2.style("fill", "black");
}
var transition = d3.transition()
.duration(d3.event.altKey ? 7500 : 750)
.tween("zoom", function (d) {
var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
return function (t) { zoomTo(i(t)); };
});
transition.selectAll("text")
.filter(function (d) { return d.parent === focus || this.style.display === "inline"; })
.style("fill-opacity", function (d) { return d.parent === focus ? 1 : 0; })
.each("start", function (d) { if (d.parent === focus)
this.style.display = "inline"; })
.each("end", function (d) { if (d.parent !== focus)
this.style.display = "none"; });
}
function zoomTo(v) {
var k = diameter / v[2];
view = v;
node.attr("transform", function (d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
circle.attr("r", function (d) { return d.r * k; });
}
});
}
d3.select(self.frameElement).style("height", diameter + "px");
我遇到的问题是当我不断地将新数据流式传输到 UI 时。每次我用一个完整的 json 文件调用 "Draw",其中包含我在数据库中的所有数据。我读过 enter()、exit()、remove() 负责更新 DOM 元素,但似乎我遗漏了一些东西。当调用 draw() 函数时,它似乎在现有气泡上方绘制气泡,并且圆形边框颜色或文本边框颜色等内容消失。 我想知道我是否以正确的方式使用 enter() 和 exit() 命令。我想让 draw() 函数接收 json 文件,并且只更新气泡图中的新信息并且不存在 DOM 元素,但它似乎只是重新创建所有 DOM 个元素。
提前谢谢你, 乔治
PS:我有两个 svg,因为我试图将屏幕一分为二,左侧被气泡图占据,右侧显示额外信息,当我们将它放大到内部时气泡
EDIT1:这是我传递给 draw() 函数的数据示例:
{"name": "",
"children":
[{"name": "10.0.0.64", "children":
[{"name": "unidentified", "children":
[{"name": "datasource", "size": 0.002551020408163265}, {"name": "datasource", "size": 0.0031746031746031746}, {"name": "datasource", "size": 0.0017123779389460522}, {"name": "average", "size": 0.008550406895439414}, {"name": "datasource", "size": 0.019020266320109332}]},
{"name": "www.quefaire.be", "children":
[{"name": "average", "size": 0.002270490544565449}, {"name": "datasource", "size": 0.023243328100470965}]}, {"name": "tnsinternet.be", "children": [{"name": "average", "size": 0.002199967070440535}, {"name": "datasource", "size": 0.022009167303284966}]},
{"name": "hy.sachinese.com", "children":
[{"name": "average", "size": 0.0022121416372369493}, {"name": "datasource", "size": 0.022222222222222223}]}, {"name": "pagesdor.be", "children": [{"name": "average", "size": 0.0030411250824668935}, {"name": "datasource", "size": 0.03672943251374624}]},
{"name": "r.254a.com", "children":
[{"name": "average", "size": 0.0018942905665264935}, {"name": "datasource", "size": 0.015873015873015872}]},
{"name": "i.ctnsnet.com", "children":
[{"name": "average", "size": 0.0018942905665264935}, {"name": "datasource", "size": 0.015873015873015872}]},
{"name": "link.carrefour.eu", "children":
[{"name": "average", "size": 0.0019793245801319357}, {"name": "datasource", "size": 0.017857142857142856}]},
{"name": "goudengids.be", "children":
[{"name": "average", "size": 0.00457041828122788}, {"name": "datasource", "size": 0.06349206349206349}]}, {"name": "www.inmemoriam.be", "children": [{"name": "average", "size": 0.0018443560093702978}, {"name": "datasource", "size": 0.014707876206037973}]},
{"name": "www1.gfk-wi.com", "children":
[{"name": "datasource", "size": 0.011986645572622365}, {"name": "average", "size": 0.0017277318393667718}]}, {"name": "bootstrapcdn.com", "children": [{"name": "average", "size": 0.008550406895439414}, {"name": "datasource", "size": 0.13314186424076532}]},
{"name": "maxcdn.bootstrapcdn.com", "children":
[{"name": "datasource", "size": 0.031746031746031744}, {"name": "average", "size": 0.0027563593243117796}]}]}]}
这也是我的 CSS:
.svg-container {
display: flex;
position: relative;
width: 100%;
padding-bottom: 100%; /* aspect ratio */
vertical-align: top;
overflow: hidden;
}
.svg-content-responsive {
display: inline-block;
position: absolute;
top: 10px;
left: 0;
}
.container {
float: left;
}
.node {
cursor: pointer;
stroke: darkgrey;
}
.node:hover {
stroke: #000;
stroke-width: 1.5px;
}
.node--leaf {
fill: white;
}
.label {
font: 11px "Helvetica Neue", Helvetica, Arial, sans-serif;
text-anchor: middle;
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff, 0 -1px 0 #fff;
}
.label,
.node--root,
.node--leaf {
pointer-events: none;
}
首先确保所有的节点都有唯一的名字或者必须有一些id来唯一定义一个节点。 使用如下所示的唯一键来唯一标识数据。 在您的数据集中,我的解决方案将不起作用,原因是很少有节点具有非唯一名称,例如数据集等。
以下解决方案只有在名称唯一时才有效。
而不是
var circle = svg.selectAll("circle").data(nodes)
;
这样做
var circle = svg.selectAll("circle").data(nodes, function(d){return d.name});
而不是这个
var text = svg.selectAll("text").data(nodes);
这样做
var text = svg.selectAll("text").data(nodes, function(d){return d.name});
exit()
功能因上述原因无法正常工作
了解关键功能 here