如何在动态变化的d3 vis中根据其数据值重新排列svg元素的DOM顺序?

时间:2019-04-10 06:56:36

标签: javascript d3.js dom svg

我有一个json数据集,可以使用d3.js库以与this example类似的方式动态地可视化。

但是,我希望将其在图表中的位置更改为较低位置的柱位于较高位置的柱的“后面”。为此,我想基于数据设置z索引(值较低的条具有较低的z索引,因此将位于值较大的条之后)。不幸的是,SVG使用文档顺序作为z索引,这就是为什么每次图表更改时我都需要在文档内重新排列条形图(示例中的chartRow变量)的原因。

有什么办法可以实现这个目标?

1 个答案:

答案 0 :(得分:1)

在DOM中对元素进行重新排序

要使用d3对DOM中的元素重新排序,可以使用selection.sortselection.order

// Sort & then order:
selection.sort(function(a,b) {
  return a.property - b.property; 
})
.order();

Selection.sort根据绑定数据对选择进行排序,ab分别表示绑定到单个元素的数据。产生的行为与Array.sort本质上相同,但是不是使用数组,而是使用绑定数据对选择中的元素进行排序。提供给selection.sort的功能内容(比较功能)确定排序顺序:

  

比较函数(默认为升序)传递了两次   要比较的元素的数据a和b。它应该返回一个负数,   正值或零值。如果为负,则a应该在b之前;否则为a。如果   正数,则a应该在b之后;否则,a和b被认为   相等,顺序是任意的。 (docs

一旦我们有了排序的选择,就可以应用selection.order(),它会根据选择的顺序将每个元素重新插入DOM中。请记住,先绘制的元素可以被后来绘制的元素覆盖-选择中的第一个元素意味着可能在所有内容的下面。


因此,如果我们有类似的情况,则根据数据数组的顺序依次绘制重叠的条形:

enter image description here

var bars = [
  {value: 100},
  {value: 50},
  {value: 200},
  {value: 70},
  {value: 40},
  {value: 120}
];

var height = 200;
var width = 500;

var svg = d3.select("body")
  .append("svg")
  .attr("height",height)
  .attr("width",width);

var bars = svg.selectAll("rect")
  .data(bars)
  .enter()
  .append("rect")
  .attr("width", 45)
  .attr("height", function(d) { return d.value; })
  .attr("y", function(d) { return height-d.value; })
  .attr("x", function(d,i) { return i * 30 + 50; })
  .attr("fill", function(d,i){ return d3.schemeCategory10[i]; })
  .attr("stroke","black")
  .attr("stroke-width",1);


// Order the bars:
bars.sort(function(a,b) {
  return b.value - a.value;
}).order();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

我们可以拉起小条,例如通过排序和顺序来提高可见度:

enter image description here

var bars = [
  {value: 100},
  {value: 50},
  {value: 200},
  {value: 70},
  {value: 40},
  {value: 120}
];

var height = 200;
var width = 500;

var svg = d3.select("body")
  .append("svg")
  .attr("height",height)
  .attr("width",width);

var bars = svg.selectAll("rect")
  .data(bars)
  .enter()
  .append("rect")
  .attr("width", 45)
  .attr("height", function(d) { return d.value; })
  .attr("y", function(d) { return height-d.value; })
  .attr("x", function(d,i) { return i * 30 + 50; })
  .attr("fill", function(d,i){ return d3.schemeCategory10[i]; })
  .attr("stroke","black")
  .attr("stroke-width",1);

  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>


根据订单重新定位

正如您在上面看到的那样,这不会重置每个条形的x或y属性以反映z的顺序(如您的示例)。就像我上面的基本示例一样,我们可以基于i重置x值(也可以通过重新缩放来完成,但足以作为单独的问答):

var bars = [
  {value: 100},
  {value: 50},
  {value: 200},
  {value: 70},
  {value: 40},
  {value: 120}
];

var height = 200;
var width = 500;

var svg = d3.select("body")
  .append("svg")
  .attr("height",height)
  .attr("width",width);

var bars = svg.selectAll("rect")
  .data(bars)
  .enter()
  .append("rect")
  .attr("width", 45)
  .attr("height", function(d) { return d.value; })
  .attr("y", function(d) { return height-d.value; })
  .attr("x", function(d,i) { return i * 30 + 50; })
  .attr("fill", function(d,i){ return d3.schemeCategory10[i]; })
  .attr("stroke","black")
  .attr("stroke-width",1)
  
bars = bars.sort(function(a,b) {
  return a.value - b.value;
}).order();

bars.transition()
  .attr("x", function(d,i) { return i * 30 + 50; })
  .delay(500)
  .duration(2000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>


另请参见:此question和答案与您的示例有很多相似之处:在转换小节的长度和小节的顺序时,使用了排序和排序。