d3.js - 从数组生成颜色 table

d3.js - generate color table from array

我想生成多行的 d3 颜色 table,在下面的代码中,看起来颜色 table 根本没有更新。

color_table()
function color_table() {
 var categorical = [
  { "name" : "schemeAccent", "n": 8},
  { "name" : "schemeDark2", "n": 8},
  { "name" : "schemePastel2", "n": 8},
  { "name" : "schemeSet2", "n": 8},
  { "name" : "schemeSet1", "n": 9},
  { "name" : "schemePastel1", "n": 9},
  { "name" : "schemeCategory10", "n" : 10},
  { "name" : "schemeSet3", "n" : 12 },
  { "name" : "schemePaired", "n": 12},
  //{ "name" : "schemeCategory20", "n" : 20 },
  //{ "name" : "schemeCategory20b", "n" : 20},
  //{ "name" : "schemeCategory20c", "n" : 20 }
]

var width = 400,
    height = 500;

var colorScale = null

var n = 0,
    unit = 0

var svg = d3.select('body').selectAll('cols')
  .data(categorical)
  .enter()
  .append('svg')
  .attr('width',width)
  .attr('height',height/categorical.length)
  .style('border','1px solid red')
  
var bars = svg.selectAll(".bars")
    .data((d,i) => {
      n = d.n
      unit = width/n
      //console.log(n)
      colorScale = d3.scaleOrdinal(d3[d.name]); 
      return d3.range(n)})
  .enter().append("rect")
    .attr("class", "bars")
    .attr("x", function(d, i) { return i * unit; })
    .attr("y", 0)
    .attr("height", height)
    .attr("width", unit)
    .style("fill", (d,i) => colorScale(d) )
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>

你的色标都是一样的,因为你为每个色标引用了相同的变量:colorScale。通过为每个数据评估 colorScale = d3.scaleOrdinal(d3[d.name]); 并且仅在之后添加矩形,它们根据最后处理的数据着色以定义 colorScale。这也是为什么每个系列都有相同的矩形宽度。

要解决这个问题,我们可以使用 d3.local,它似乎是为这种情况设计的:

D3 locals allow you to define local state independent of data. For instance, when rendering small multiples of time-series data, you might want the same x-scale for all charts but distinct y-scales to compare the relative performance of each metric. (docs)

使用 d3 local 我们可以为每个系列保存不同的色标和宽度值:

var localScale = d3.local();
var localWidth = d3.local();

并且我们可以使用 local.set 为每个父节点设置它们的值,如下所示,它为指定节点设置一个值(分别为第 2 个和第 1 个参数):

var svg = d3.select('body').selectAll('cols')
  .data(categorical)
  .enter()
  .append('svg')
  .each(function(d) {
    localScale.set(this,d3.scaleOrdinal(d3[d.name]));
    localWidth.set(this,width/d.n);
  })

要获取值,我们可以对子节点使用 local.get(node)。虽然我们没有为子节点定义本地值,但 local.get 会找到具有定义值的最近祖先,因此我们可以将矩形提供给 local.get:

var bars = svg.selectAll(".bars")
   .data(d => d3.range(d.n))
   .enter().append("rect")
   .attr("x", function(d, i) { 
        return i * localWidth.get(this); 
   })
   .attr("width", function() {
     return localWidth.get(this)
   })
   .style("fill", function(d) {
      return localScale.get(this)(d) ;
  })
  ...

我已将传递的数据更改为简单的 d3.range(d.n),因为我们不再需要在这里计算任何其他内容。

总而言之,我们得到:

color_table()
function color_table() {
 var categorical = [
  { "name" : "schemeAccent", "n": 8},
  { "name" : "schemeDark2", "n": 8},
  { "name" : "schemePastel2", "n": 8},
  { "name" : "schemeSet2", "n": 8},
  { "name" : "schemeSet1", "n": 9},
  { "name" : "schemePastel1", "n": 9},
  { "name" : "schemeCategory10", "n" : 10},
  { "name" : "schemeSet3", "n" : 12 },
  { "name" : "schemePaired", "n": 12},
]

var width = 400,
    height = 500;

var localScale = d3.local();
var localWidth = d3.local();

var n = 0,
    unit = 0

var svg = d3.select('body').selectAll('cols')
  .data(categorical)
  .enter()
  .append('svg')
  .attr('width',width)
  .attr('height',height/categorical.length)
  .style('border','1px solid red')
  .each(function(d) {
    localScale.set(this,d3.scaleOrdinal(d3[d.name]));
    localWidth.set(this,width/d.n);
  })
  
var bars = svg.selectAll(".bars")
    .data(d => d3.range(d.n))
  .enter().append("rect")
    .attr("class", "bars")
    .attr("x", function(d, i) { return i * localWidth.get(this); })
    .attr("y", 0)
    .attr("height", height)
    .attr("width", function() {
      return localWidth.get(this)
    })
    .style("fill", function(d) {
        return localScale.get(this)(d) ;
    })
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>

提供:

另一种方法是将比例存储在数据本身(父数据或子数据)上,但 d3.local 是为此目的而设计的,可能是最简单的解决方案。