d3.js sunburst具有动态构建的可更新数据结构(d3.js sunburst with dynamically built updatable data structure)

编程入门 行业动态 更新时间:2024-10-25 23:36:48
d3.js sunburst具有动态构建的可更新数据结构(d3.js sunburst with dynamically built updatable data structure)

我正在尝试使用d3.js创建一个完全动态的Sunburst图。 我找到的示例和教程倾向于使用现有/完全填充的数据结构,这些数据结构可能能够修改现有弧的值,但不允许根据需要添加子弧。

同样,我找到的教程允许新数据集只是替换现有结构并从头开始绘制。 这不是我试图实现的行为。

我需要的是一个动态构建的图表,它基于提供的输入数据。

我可以将子项附加到数据集的末尾,转换并呈现​​结果而不会出现问题。 问题发生在我在现有结构中的某个地方插入一个孩子时,d3的selectAll()没有按预期运行。 它包括新的弧(尚未绘制),导致任何剩余的弧被错误地渲染。 然后当转换弧时,它似乎得到弧Dom ID和它所代表的数据混合起来。 新弧不可见,并且存在应放置新弧的空白空间。

要明确我的意图是:

添加到现有数据结构,允许在提供新信息时添加新子项 在创建和绘制新弧之前转换现有弧开放空间

分为jsfiddle示例的四个步骤:

图的初始化(绘制一个不可见的“根”弧)

{name:“a_0”,孩子:[]}

添加First Child数据并将其子项添加到root

{name:“a_0”,儿童:[{name:“a_1”,儿童:[{name:“a_2”,儿童:[{name:“a_3”}]}]}]}

将第二个子项和基础子项添加到root

{name:“a_0”,儿童:[{姓名:“a_1”,儿童:[{姓名:“a_2”,儿童:[{姓名:“a_3”}]}]},{姓名:“a_4”,儿童:[{name:“a_5”,children:[{name:“a_6”}]}]}]}

在现有弧a_2中插入另一个子节点

{name:“a_0”,儿童:[{name:“a_1”,儿童:[{name:“a_2”,儿童:[{name:“a_3”},{name:“a_7”}]}]}, {name:“a_4”,children:[{name:“a_5”,children:[{name:“a_6”}]}]}]}

第1步工作正常

第2步正确绘制弧线

步骤3转换现有弧并将新弧添加到图形中

第4步导致一些意外行为。

在现有和进入新弧线的过程中,一些弧线“跳跃”失去与各自数据的正确关联。最终结果似乎是:

a_0 - 是正确的 a_1&a_2 - 看起来正确 a_3 - 已缩小以容纳新兄弟a_7 - 预期的行为 a_4 - 消失了 a_5 - 跳转到a_4应该是的位置 a_6 - (看起来像)它是重复的并且存在于应该存在的位置和a_5应该存在的位置 a_7 - 未显示,它应该是空的位置,并且似乎与a_6数据相关联

最终结果是什么样的,真正发生的事情是不一样的。

在尝试更新图形时,现有弧的selectAll()包括(a_0,a_1,a_2,a_3,a_4,a_5,a_7)。 如果现有的a_6不包含在selectAll()中,但a_7(尚未绘制)是。 enter()函数似乎对现有的a_6进行操作,然后将其视为新弧

看起来我在正确的轨道上一直到a_6,但我还没有想出添加a_7时行为的原因。

jsFidde执行上述步骤,包括:

每个弧的独特颜色 显示每个弧名称的表格, 如果弧由d3js'selectAll()(即“现有”)或enter()(即“new”)处理, 绘制现有弧线或新弧线时当前正在分配的d3索引。 任何过渡后每个弧应出现的预期目标位置, 作为Arc的Arctween信息正在从其以前的位置转换到新位置

问题:

发生什么事会导致第4步中的这种行为? 有没有办法确保每个弧与它代表的数据之间的完整性? 有没有办法将子项插入现有结构或更新此动态庄园中的图形?

关于jsfiddle的工作示例https://jsfiddle.net/mfitzgerald/j2eowwya/

var dataObj = { name:"a_0", color: "none" }; var height = 300; var width = 500; var radius = Math.min(width, height) / 2; var graph = d3.select("#graph") .attr('height', height) .attr('width', width) .append("g") .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); var partition = d3.layout.partition() .sort(null) .size([2 * Math.PI, radius * radius]) .value(function(d, i) { return 1; }); var arc = d3.svg.arc() .startAngle(function(d) { if (isNaN(d.x)) { d.x = 0; } d.x0 = d.x; return d.x; }) .endAngle(function(d) { if (isNaN(d.dx)) { d.dx = 0; } d.dx0 = d.dx; return d.x + d.dx; }) .innerRadius(function(d) { if (isNaN(d.y)) { d.y = 0; } d.y0 = d.y; return Math.sqrt(d.y); }) .outerRadius(function(d) { if (isNaN(d.dy)) { d.dy = 0; } d.dy0 = d.dy; return Math.sqrt(d.y + d.dy); }); var arcTween = function(a) { var i = d3.interpolate({x: a.x0, dx: a.dx0, y: a.y0, dy: a.dy0}, a); return function(t) { var b = i(t); a.x0 = b.x; a.dx0 = b.dx; a.y0 = b.y; a.dy0 = b.dy; displayStats("arctween", b); return arc(b); }; } // Root Arc graph.datum(dataObj).selectAll('path.arc') .data(partition.nodes) .enter() .append('path') .attr('class', function(d) { return "arc " + d.name; }) .attr("d", arc) .attr("id", function(d, i) { return "path_"+i; }) .attr("name", function(d) { return d.name; }) .style("fill", "none"); function updateGraph() { console.log("Update Graph"); console.log(dataObj); var update = graph.datum(dataObj).selectAll('path.arc') .data(partition.nodes); // Move existing Arcs update.each(function(d, i) { displayStats("target", d, i, "existing"); var domId = $(this).attr("id"); console.log("["+i+"] Exist Arc name:"+d.name+", dom_id:"+domId); }) .transition() .delay(function(d, i) { return i * 250; }) .duration(1500) .attrTween("d", arcTween); // Add New Arcs update.enter().append('path') .attr('class', function(d, i) { return "arc "+d.name; }) .attr("d", arc) .attr("id", function(d, i) { var domId = "path_"+i; console.log("["+i+"] NEW Arc name:"+d.name+", dom_id:"+domId); displayStats("target", d, i, "new"); return domId; }) .style("stroke", "#fff") .style("fill", function(d) { return d.color; }) .style("opacity", 0) .transition() .delay(function(d, i) { return i * 250; }) .duration(1500) .style("opacity", .5) .attrTween("d", arcTween); }

I am attempting to create a fully dynamic Sunburst graph using d3.js. The examples and tutorials I have located tend to use existing/fully-populated data structures which may have the ability to modify the value of existing arcs but does not allow the ability to add child arcs as needed.

Likewise the tutorials I have located which allow new datasets simply replace the existing structure and begin drawing from scratch. This is not the behavior I am trying to implement.

What I need is a dynamically built graph based on incoming data as it is provided.

I am able to append children to the end of the data set, transition and render the results without issue. The problem occurs any time I insert a child somewhere within the existing structure, d3’s selectAll() does not function as expected. It includes the new arc (which has yet to be drawn) resulting in any remaining arcs being rendered incorrectly. Then when transitioning the arcs it seems to get the arcs Dom ID and data it supposedly represents gets mixed up. The new arc is not visible and an empty space exists where new arc should be placed.

To be clear my intent is:

Add to the existing data structure allowing new children to be added when new information is provided To transition existing arcs opening space for the new arcs before they are created and drawn

Broken down into four steps of the jsfiddle example:

Initialization of the graph (draws an invisible “root” arc)

{ name:"a_0", children: [] }

Adding First Child data and it’s children to root

{ name:"a_0", children:[ { name:"a_1", children:[ { name:"a_2", children:[ { name:"a_3" } ] } ] } ] }

Adding Second Child and underlying children to root

{ name:"a_0", children:[ { name:"a_1", children:[ { name:"a_2", children:[ { name:"a_3" } ] } ] }, { name:"a_4", children:[ { name:"a_5", children:[ { name:"a_6" } ] } ] } ] }

Inserting another child within the existing arc a_2

{ name:"a_0", children:[ { name:"a_1", children:[ { name:"a_2", children:[ { name:"a_3" }, { name:"a_7" } ] } ] }, { name:"a_4", children:[ { name:"a_5", children:[ { name:"a_6" } ] } ] } ] }

Step 1 works just fine

Step 2 draws the arcs properly

Step 3 transitions the existing arcs and adds the new arcs to the graph

Step 4 results some unexpected behavior.

During the transition of existing and entering of new arcs some of the arcs "jump around" losing the proper association with their respective data The end result appears to be:

a_0 - is correct a_1 & a_2 - look correct a_3 - has shrunk to accommodate the new sibling a_7 - expected behavior a_4 - disappears a_5 - jumps down where a_4 should be a_6 - (looks like) it is duplicated and exists once where it should be and where a_5 should be a_7 - not displayed, location where it should be is empty space and appears to be associated with a_6 data

What the end result looks like and what is really going on are not the same.

In the attempt to update the graph the selectAll() for the existing arcs includes (a_0, a_1, a_2, a_3, a_4, a_5, a_7). Where the existing a_6 is not included in the selectAll() but a_7 (which has not been drawn) is. The enter() function appears to operate on the existing a_6 which is then treated as a new arc

It looked like I was on the right track getting all the way to a_6, but I have not figured out the reason for the behavior when adding a_7.

The jsFidde executes the steps as described above including:

Unique colors for each arc A table displaying the name of each arc, If the arc is being handled by d3js' selectAll() (i.e. "existing") or enter() (i.e. "new"), The d3 Index as it is currently being assigned when drawing existing or new arcs. Expected target position where each arc should appear after any transitioning, Arctween information as an Arc is being transitioned from its former location to the new location and

Questions:

What is going on that would cause this behavior in Step 4? Is there a way to ensure the integrity between each arc and the data it represents? Is there a way to insert children into the existing structure or update the graph in this dynamic manor?

Working example on jsfiddle https://jsfiddle.net/mfitzgerald/j2eowwya/

var dataObj = { name:"a_0", color: "none" }; var height = 300; var width = 500; var radius = Math.min(width, height) / 2; var graph = d3.select("#graph") .attr('height', height) .attr('width', width) .append("g") .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); var partition = d3.layout.partition() .sort(null) .size([2 * Math.PI, radius * radius]) .value(function(d, i) { return 1; }); var arc = d3.svg.arc() .startAngle(function(d) { if (isNaN(d.x)) { d.x = 0; } d.x0 = d.x; return d.x; }) .endAngle(function(d) { if (isNaN(d.dx)) { d.dx = 0; } d.dx0 = d.dx; return d.x + d.dx; }) .innerRadius(function(d) { if (isNaN(d.y)) { d.y = 0; } d.y0 = d.y; return Math.sqrt(d.y); }) .outerRadius(function(d) { if (isNaN(d.dy)) { d.dy = 0; } d.dy0 = d.dy; return Math.sqrt(d.y + d.dy); }); var arcTween = function(a) { var i = d3.interpolate({x: a.x0, dx: a.dx0, y: a.y0, dy: a.dy0}, a); return function(t) { var b = i(t); a.x0 = b.x; a.dx0 = b.dx; a.y0 = b.y; a.dy0 = b.dy; displayStats("arctween", b); return arc(b); }; } // Root Arc graph.datum(dataObj).selectAll('path.arc') .data(partition.nodes) .enter() .append('path') .attr('class', function(d) { return "arc " + d.name; }) .attr("d", arc) .attr("id", function(d, i) { return "path_"+i; }) .attr("name", function(d) { return d.name; }) .style("fill", "none"); function updateGraph() { console.log("Update Graph"); console.log(dataObj); var update = graph.datum(dataObj).selectAll('path.arc') .data(partition.nodes); // Move existing Arcs update.each(function(d, i) { displayStats("target", d, i, "existing"); var domId = $(this).attr("id"); console.log("["+i+"] Exist Arc name:"+d.name+", dom_id:"+domId); }) .transition() .delay(function(d, i) { return i * 250; }) .duration(1500) .attrTween("d", arcTween); // Add New Arcs update.enter().append('path') .attr('class', function(d, i) { return "arc "+d.name; }) .attr("d", arc) .attr("id", function(d, i) { var domId = "path_"+i; console.log("["+i+"] NEW Arc name:"+d.name+", dom_id:"+domId); displayStats("target", d, i, "new"); return domId; }) .style("stroke", "#fff") .style("fill", function(d) { return d.color; }) .style("opacity", 0) .transition() .delay(function(d, i) { return i * 250; }) .duration(1500) .style("opacity", .5) .attrTween("d", arcTween); }

最满意答案

@Gordon回答了这个问题。 在updateGraph代码中加入.data()时,通过添加键函数解决了该问题。

在jsfiddle的分叉示例

var update = graph.datum(dataObj).selectAll('path.arc') .data(partition.nodes, function(d) { return d.name; } );

我相信这些问题的答案是:

.data()函数使用索引数组,该数组仅在给定任何新弧附加到数组末尾时唯一标识每个弧。 一旦插入一个,这将导致数据,绘制的弧和相关的DOM ID未对齐。 根据Gordon的建议,使用密钥功能可以对特定节点进行唯一标识,从而保持数据和图形按预期同步。

更新

需要进行另外的修改,因为DOM id是由数据元素的数组索引设置的,但仍然存在与DOM和基础图/数据的无效关联。

这将导致2 a_4 DOM id。 而不是使用节点名称作为DOM ID的数组索引应该保持这种关联正确。

@Gordon has answered the question. The issue was resolved by adding a key function when joining with .data() in the updateGraph code.

Forked example on jsfiddle

var update = graph.datum(dataObj).selectAll('path.arc') .data(partition.nodes, function(d) { return d.name; } );

I believe the answers to the questions are:

The .data() function uses an indexed array which only uniquely identifies each arc given any new arcs are appended to the end of the array. Once one is inserted this would cause the data, graphed arcs and associated DOM ids to be misaligned. Using the key function, as suggested by Gordon, allows unique identification of specific nodes keeping the Data and Graph in sync as expected.

Update

An additional modification would need to be made as the DOM id was set by the array index of the data element there would still be an invalid association with the DOM and the underlying graph/data.

This would result in 2 a_4 DOM id's. Instead of using the array index using the Node Name as the DOM id should keep this association correct.

更多推荐

本文发布于:2023-08-02 21:34:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1381475.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:数据结构   动态   js   sunburst   structure

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!