如果源 json 更新(添加或删除项目),则动态更新 D3 Sunburst
Posted
技术标签:
【中文标题】如果源 json 更新(添加或删除项目),则动态更新 D3 Sunburst【英文标题】:Dynamically update D3 Sunburst if the source json is updated (item added or deleted) 【发布时间】:2016-06-29 23:34:39 【问题描述】:我是 D3 的新手,如果源 json 被修改,我会尝试动态更新图表。但我无法做到这一点。
请查看this plunkr
Js:
var width = 500,
height = 500,
radius = Math.min(width, height) / 2;
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.sqrt()
.range([0, radius]);
var color = d3.scale.category10();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2 + 10) + ") rotate(-90 0 0)");
var partition = d3.layout.partition()
.value(function(d)
return d.size;
);
var arc = d3.svg.arc()
.startAngle(function(d)
return Math.max(0, Math.min(2 * Math.PI, x(d.x)));
)
.endAngle(function(d)
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
)
.innerRadius(function(d)
return Math.max(0, y(d.y));
)
.outerRadius(function(d)
return Math.max(0, y(d.y + d.dy));
);
//d3.json("/d/4063550/flare.json", function(error, root)
var root = initItems;
var g = svg.selectAll("g")
.data(partition.nodes(root))
.enter().append("g");
var path = g.append("path")
.attr("d", arc)
.style("fill", function(d)
return color((d.children ? d : d.parent).name);
)
.on("click", click)
.each(function(d)
this.x0 = d.x;
this.dx0 = d.dx;
);
//.append("text")
var text = g.append("text")
.attr("x", function(d)
return y(d.y);
)
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.attr("transform", function(d)
return "rotate(" + computeTextRotation(d) + ")";
)
.text(function(d)
return d.name;
)
.style("fill", "white");
function computeTextRotation(d)
var angle = x(d.x + d.dx / 2) - Math.PI / 2;
return angle / Math.PI * 180;
function click(d)
console.log(d)
// fade out all text elements
if (d.size !== undefined)
d.size += 100;
;
text.transition().attr("opacity", 0);
path.transition()
.duration(750)
.attrTween("d", arcTween(d))
.each("end", function(e, i)
// check if the animated element's data e lies within the visible angle span given in d
if (e.x >= d.x && e.x < (d.x + d.dx))
// get a selection of the associated text element
var arcText = d3.select(this.parentNode).select("text");
// fade in the text element and recalculate positions
arcText.transition().duration(750)
.attr("opacity", 1)
.attr("transform", function()
return "rotate(" + computeTextRotation(e) + ")"
)
.attr("x", function(d)
return y(d.y);
);
);
//);
// Word wrap!
var insertLinebreaks = function(t, d, width)
alert(0)
var el = d3.select(t);
var p = d3.select(t.parentNode);
p.append("g")
.attr("x", function(d)
return y(d.y);
)
// .attr("dx", "6") // margin
//.attr("dy", ".35em") // vertical-align
.attr("transform", function(d)
return "rotate(" + computeTextRotation(d) + ")";
)
//p
.append("foreignObject")
.attr('x', -width / 2)
.attr("width", width)
.attr("height", 200)
.append("xhtml:p")
.attr('style', 'word-wrap: break-word; text-align:center;')
.html(d.name);
alert(1)
el.remove();
alert(2)
;
//g.selectAll("text")
// .each(function(d,i) insertLinebreaks(this, d, 50 ); );
d3.select(self.frameElement).style("height", height + "px");
// Interpolate the scales!
function arcTween(d)
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, 1]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(d, i)
return i ? function(t)
return arc(d);
: function(t)
x.domain(xd(t));
y.domain(yd(t)).range(yr(t));
return arc(d);
;
;
function arcTweenUpdate(a)
console.log(path);
var _self = this;
var i = d3.interpolate( x: this.x0, dx: this.dx0 , a);
return function(t)
var b = i(t);
console.log(window);
_self.x0 = b.x;
_self.dx0 = b.dx;
return arc(b);
;
setTimeout(function()
path.data(partition.nodes(newItems))
.transition()
.duration(750)
.attrTween("d", arcTweenUpdate)
, 2000);
【问题讨论】:
【参考方案1】:除了@Cyril 建议删除以下行:
d3.select(self.frameElement).style("height", height + "px");
我在你的小提琴中做了进一步的修改:working fiddle
这里使用的想法是添加一个函数updateChart
,它接受items
,然后生成图表:
var updateChart = function (items)
// code to update the chart with new items
updateChart(initItems);
setTimeout(function () updateChart(newItems); , 2000);
这不使用您创建的arcTweenUpdate
函数,但我将尝试解释基本概念:
首先,您需要使用现有数据JOIN
新数据:
// DATA JOIN - Join new data with old elements, if any.
var gs = svg.selectAll("g").data(partition.nodes(root));
然后,ENTER
在需要时创建新元素:
// ENTER
var g = gs.enter().append("g").on("click", click);
但是,我们还需要 UPDATE
现有/新的 path
和 text
具有新数据的节点:
// UPDATE
var path = g.append("path");
gs.select('path')
.style("fill", function(d)
return color((d.children ? d : d.parent).name);
)
//.on("click", click)
.each(function(d)
this.x0 = d.x;
this.dx0 = d.dx;
)
.transition().duration(500)
.attr("d", arc);
var text = g.append("text");
gs.select('text')
.attr("x", function(d)
return y(d.y);
)
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.attr("transform", function(d)
return "rotate(" + computeTextRotation(d) + ")";
)
.text(function(d)
return d.name;
)
.style("fill", "white");
并且,在创建/更新所有内容后,删除未被使用的 g
节点,即 EXIT
:
// EXIT - Remove old elements as needed.
gs.exit().transition().duration(500).style("fill-opacity", 1e-6).remove();
JOIN + ENTER + UPDATE + EXIT
的整个模式在 Mike Bostock 的以下文章中得到了展示:
-
General Update Pattern - I
General Update Pattern - II
General Update Pattern - III
【讨论】:
很好的答案。我们如何为图表的每个部分提供工具提示(名称和值)?【参考方案2】:在小提琴旁边,setTimeout
没有运行,因为:
d3.select(self.frameElement).style("height", height + "px");
你会得到Uncaught SecurityError: Failed to read the 'frame' property from 'Window': Blocked a frame with origin "https://fiddle.jshell.net" from access a frame with origin 并且 setTimeout 永远不会得到调用。
所以你可以删除这行 d3.select(self.frameElement).style("height", height + "px");
只是为了小提琴。
除此之外:
你的超时函数应该是这样的:
setTimeout(function()
//remove the old graph
svg.selectAll("*").remove();
root = newItems;
g = svg.selectAll("g")
.data(partition.nodes(newItems))
.enter().append("g");
/make path
path = g.append("path")
.attr("d", arc)
.style("fill", function(d)
return color((d.children ? d : d.parent).name);
)
.on("click", click)
.each(function(d)
this.x0 = d.x;
this.dx0 = d.dx;
);
//make text
text = g.append("text")
.attr("x", function(d)
return y(d.y);
)
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.attr("transform", function(d)
return "rotate(" + computeTextRotation(d) + ")";
)
.text(function(d)
return d.name;
)
.style("fill", "white");
工作小提琴here
【讨论】:
我需要一种解决方案,即我不会完全删除整个图表并重新绘制整个图表,而是应该类似于点击我们使用 Twean 动画更新图表时发生的情况。【参考方案3】:为了让 enter() 和转换起作用,您需要为 d3 提供一种方法来识别数据中的每个项目。 .data() 函数有第二个参数,可以让你返回一些东西作为 id 使用。 enter() 将使用 id 来决定对象是否是新的。
尝试改变
path.data(partition.nodes(newItems))
.data(partition.nodes(root));
到
path.data(partition.nodes(newItems), function(d)return d.name);
.data(partition.nodes(root), function(d)return d.name);
【讨论】:
以上是关于如果源 json 更新(添加或删除项目),则动态更新 D3 Sunburst的主要内容,如果未能解决你的问题,请参考以下文章